| 
									
										
										
										
											2017-04-14 10:29:00 +02:00
										 |  |  | // Copyright 2017 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2017-04-14 10:29:00 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2017-04-14 10:29:00 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2017-04-14 10:29:00 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2017-04-14 10:29:00 +02:00
										 |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | package http | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2018-08-08 09:33:06 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	"html/template" | 
					
						
							|  |  |  | 	"path" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/swarm/api" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type htmlListData struct { | 
					
						
							|  |  |  | 	URI  *api.URI | 
					
						
							|  |  |  | 	List *api.ManifestList | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  | var TemplatesMap = make(map[string]*template.Template) | 
					
						
							|  |  |  | var faviconBytes []byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	for _, v := range []struct { | 
					
						
							|  |  |  | 		templateName string | 
					
						
							|  |  |  | 		partial      string | 
					
						
							|  |  |  | 		funcs        template.FuncMap | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			templateName: "error", | 
					
						
							|  |  |  | 			partial:      errorResponse, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			templateName: "bzz-list", | 
					
						
							|  |  |  | 			partial:      bzzList, | 
					
						
							| 
									
										
										
										
											2018-08-08 09:33:06 +02:00
										 |  |  | 			funcs: template.FuncMap{ | 
					
						
							|  |  |  | 				"basename": path.Base, | 
					
						
							|  |  |  | 				"leaflink": leafLink, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			templateName: "landing-page", | 
					
						
							|  |  |  | 			partial:      landing, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} { | 
					
						
							|  |  |  | 		TemplatesMap[v.templateName] = template.Must(template.New(v.templateName).Funcs(v.funcs).Parse(baseTemplate + css + v.partial + logo)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bytes, err := hex.DecodeString(favicon) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	faviconBytes = bytes | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-08 09:33:06 +02:00
										 |  |  | func leafLink(URI api.URI, manifestEntry api.ManifestEntry) string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("/bzz:/%s/%s", URI.Addr, manifestEntry.Path) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  | const bzzList = `{{ define "content" }} | 
					
						
							|  |  |  | <h3 class="top-space">Swarm index of {{ .URI }}</h3> | 
					
						
							|  |  |  | <hr> | 
					
						
							|  |  |  | <table> | 
					
						
							|  |  |  |   <thead> | 
					
						
							|  |  |  |     <tr> | 
					
						
							|  |  |  |       <th>Path</th> | 
					
						
							|  |  |  |       <th>Type</th> | 
					
						
							|  |  |  |       <th>Size</th> | 
					
						
							|  |  |  |     </tr> | 
					
						
							|  |  |  |   </thead> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   <tbody> | 
					
						
							|  |  |  |     {{ range .List.CommonPrefixes }} | 
					
						
							|  |  |  |     <tr> | 
					
						
							|  |  |  |       <td> | 
					
						
							|  |  |  |         <a class="normal-link" href="{{ basename . }}/">{{ basename . }}/</a> | 
					
						
							|  |  |  |       </td> | 
					
						
							|  |  |  |       <td>DIR</td> | 
					
						
							|  |  |  |       <td>-</td> | 
					
						
							|  |  |  |     </tr> | 
					
						
							| 
									
										
										
										
											2018-08-08 09:33:06 +02:00
										 |  |  |     {{ end }} | 
					
						
							|  |  |  |     {{ range .List.Entries }} | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  |     <tr> | 
					
						
							|  |  |  |       <td> | 
					
						
							| 
									
										
										
										
											2018-08-08 09:33:06 +02:00
										 |  |  |         <a class="normal-link" href="{{ leaflink $.URI . }}">{{ basename .Path }}</a> | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  |       </td> | 
					
						
							|  |  |  |       <td>{{ .ContentType }}</td> | 
					
						
							|  |  |  |       <td>{{ .Size }}</td> | 
					
						
							|  |  |  |     </tr> | 
					
						
							|  |  |  |     {{ end }} | 
					
						
							|  |  |  | </table> | 
					
						
							|  |  |  | <hr> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  {{ end }}` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const errorResponse = `{{ define "content" }} | 
					
						
							|  |  |  | <div class="container"> | 
					
						
							|  |  |  |     <div class="logo"> | 
					
						
							|  |  |  |         {{ template "logo" . }} | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     <div class="separate-block"> | 
					
						
							|  |  |  |       <h3>{{.Msg}}</h3> | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     <div> | 
					
						
							|  |  |  |       <h5>Error code: {{.Code}}</h5> | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     <div class="footer"> | 
					
						
							|  |  |  |       <p>{{.Timestamp}}</p> | 
					
						
							|  |  |  |       <p>Swarm: Serverless Hosting Incentivised Peer-To-Peer Storage And Content Distribution</p> | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  |   </div> | 
					
						
							|  |  |  | {{ end }}` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const landing = `{{ define "content" }} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <script type="text/javascript"> | 
					
						
							|  |  |  | function goToPage() { | 
					
						
							|  |  |  |   var page = document.getElementById('page').value; | 
					
						
							|  |  |  |   if (page == "") { | 
					
						
							|  |  |  |     var page = "theswarm.eth" | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   var address = "/bzz:/" + page; | 
					
						
							|  |  |  |   location.href = address; | 
					
						
							|  |  |  |   console.log(address) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | </script> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <div class="container"> | 
					
						
							|  |  |  | <div class="logo"> | 
					
						
							|  |  |  |   {{ template "logo" . }} | 
					
						
							|  |  |  | </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <div class="searchbar"> | 
					
						
							|  |  |  |   <form class="separate-block" action="javascript:goToPage();"> | 
					
						
							|  |  |  |     <input type="text" id="page" autofocus name="search" placeholder="Please enter an ENS name or swarm hash to retrieve .."> | 
					
						
							|  |  |  |     <button class="button" type="submit" value="submit" onclick="goToPage();">Go!</button> | 
					
						
							|  |  |  |   </form> | 
					
						
							|  |  |  | </div> | 
					
						
							|  |  |  | <div class="footer"> | 
					
						
							|  |  |  | <p>{{.Timestamp}}</p> | 
					
						
							|  |  |  | <p>Swarm: Serverless Hosting Incentivised Peer-To-Peer Storage And Content Distribution</p> | 
					
						
							|  |  |  | </div> | 
					
						
							|  |  |  | </div> | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | {{ end }}` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const baseTemplate = `<html> | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | <head> | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  |   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | 
					
						
							|  |  |  |   <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/> | 
					
						
							|  |  |  |   <meta http-equiv="X-UA-Compatible" ww="chrome=1"/> | 
					
						
							|  |  |  |   <link rel="icon" type="image/x-icon" href="favicon.ico"/> | 
					
						
							|  |  |  |   <style> | 
					
						
							|  |  |  |   {{ template "css" . }} | 
					
						
							|  |  |  |   </style> | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | </head> | 
					
						
							|  |  |  | <body> | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  |   {{ template "content" . }} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | </body> | 
					
						
							| 
									
										
										
										
											2018-02-26 02:56:40 -06:00
										 |  |  | </html> | 
					
						
							| 
									
										
										
										
											2018-08-07 11:56:55 +02:00
										 |  |  | ` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const css = `{{ define "css" }}  | 
					
						
							|  |  |  | html { | 
					
						
							|  |  |  |   font-size: 18px; | 
					
						
							|  |  |  |   font-size: 1.13rem; | 
					
						
							|  |  |  |   -webkit-text-size-adjust: 100%; | 
					
						
							|  |  |  |   -ms-text-size-adjust: 100%; | 
					
						
							|  |  |  |   font-family: Helvetica, Arial, sans-serif; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | body { | 
					
						
							|  |  |  |   background: #f6f6f6; | 
					
						
							|  |  |  |   color: #333; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | a, a:visited, a:active { | 
					
						
							|  |  |  |   color: darkorange; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | a.normal-link, a.normal-link:active { color: #0000EE; } | 
					
						
							|  |  |  | a.normal-link:visited { color: #551A8B; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | table { | 
					
						
							|  |  |  |   border-collapse: separate; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | td { | 
					
						
							|  |  |  |   padding: 3px 10px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .container { | 
					
						
							|  |  |  |   max-width: 600px; | 
					
						
							|  |  |  |   margin: 40px auto 40px; | 
					
						
							|  |  |  |   text-align: center; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .separate-block { | 
					
						
							|  |  |  |   margin: 40px 0; | 
					
						
							|  |  |  |   word-wrap: break-word; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .footer { | 
					
						
							|  |  |  |   font-size: 12px; | 
					
						
							|  |  |  |   font-size: 0.75rem; | 
					
						
							|  |  |  |   text-align: center; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .orange { | 
					
						
							|  |  |  |   color: #ffa500; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .top-space { | 
					
						
							|  |  |  |   margin-top: 20px; | 
					
						
							|  |  |  |   margin-bottom: 20px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* SVG Logos, editable */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .searchbar { | 
					
						
							|  |  |  |   padding: 20px 20px 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .logo { | 
					
						
							|  |  |  |   margin: 100px 80px 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .logo a img { | 
					
						
							|  |  |  |   max-width: 140px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Tablet < 600p*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @media only screen and (max-width: 600px) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Mobile phone < 360p*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @media only screen and (max-width: 360px) { | 
					
						
							|  |  |  |   h1 { | 
					
						
							|  |  |  |       font-size: 20px; | 
					
						
							|  |  |  |       font-size: 1.5rem; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   h2 { | 
					
						
							|  |  |  |       font-size: 0.88rem; | 
					
						
							|  |  |  |       margin: 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   .logo { | 
					
						
							|  |  |  |       margin: 50px 40px 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   .footer { | 
					
						
							|  |  |  |       font-size: 0.63rem; | 
					
						
							|  |  |  |       text-align: center; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | input[type=text] { | 
					
						
							|  |  |  |   width: 100%; | 
					
						
							|  |  |  |   box-sizing: border-box; | 
					
						
							|  |  |  |   border: 2px solid #777; | 
					
						
							|  |  |  |   border-radius: 2px; | 
					
						
							|  |  |  |   font-size: 16px; | 
					
						
							|  |  |  |   padding: 12px 20px 12px 20px; | 
					
						
							|  |  |  |   transition: border 250ms ease-in-out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | input[type=text]:focus { | 
					
						
							|  |  |  |   border: 2px solid #ffce73; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .button { | 
					
						
							|  |  |  |   background-color: #ffa500; | 
					
						
							|  |  |  |   margin: 20px 0; | 
					
						
							|  |  |  |   border: none; | 
					
						
							|  |  |  |   border-radius: 2px; | 
					
						
							|  |  |  |   color: #222; | 
					
						
							|  |  |  |   padding: 15px 32px; | 
					
						
							|  |  |  |   text-align: center; | 
					
						
							|  |  |  |   text-decoration: none; | 
					
						
							|  |  |  |   display: inline-block; | 
					
						
							|  |  |  |   font-size: 16px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | {{ end }}` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const logo = `{{ define "logo" }}  | 
					
						
							|  |  |  | <a href="/bzz:/theswarm.eth"><img src="data:image/png;base64,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 | 
					
						
							|  |  |  | {{ end }}` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const favicon = `000001000400101000000000200068040000460000002020000000002000a8100000ae0400003030000000002000a825000056150000404000000000200028420000fe3a000028000000100000002000000001002000000000004004000000000000000000000000000000000000ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff017e7e7e0362626263545454c548484849ffffff01ffffff01ffffff01ffffff01646464375b5b5bbf4545457758585809ffffff01ffffff01ffffff0164646443626262cf626262ff535353ff454545ff454545b74949492b6868681d626262a5626262fd5c5c5cff464646ff454545dd47474755ffffff01ffffff013f3f3feb565656ff636363ff535353ff464646ff3f3f3fff373737ab393939894d4d4dff626262ff5c5c5cff464646ff424242ff3a3a3af7ffffff01ffffff01383838e9353535ff424242ff474747ff383838ff353535ff363636ab35353587363636ff3a3a3aff4a4a4aff3b3b3bff353535ff363636f5ffffff01ffffff01383838e9303030ff181818ff131313ff232323ff343434ff363636ab35353587343434ff202020ff101010ff1d1d1dff303030ff373737f5ffffff01ffffff01232323c50c0c0cff0d0d0dff131313ff171717ff171717ff2929298b2727276b0f0f0ffd0d0d0dff101010ff171717ff161616ff232323d9ffffff01ffffff014d4d4d030f0f0f650c0c0ce7131313ff161616d51d1d1d4b63636363464646691717173b0d0d0dc50f0f0fff161616ef171717752e2e2e07ffffff01ffffff01ffffff01ffffff011d1d1d0f1515155360606045626262cf636363ff464646ff454545d3484848491414144d24242417ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff013c3c3c374f4f4fff636363ff636363ff464646ff464646ff3f3f3fff3c3c3c41ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff013636363d353535ff3c3c3cff575757ff363636ff181818ff282828ff37373747ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff013636363d363636ff303030ff181818ff292929ff131313ef17171771696969136565653bffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01323232371e1e1eff0d0d0dff0c0c0cff363636ff363636a3ffffff0185858515606060ff4747476bffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01111111450d0d0dd10c0c0cff1b1b1bff2a2a2a993e3e3e0b30303085292929ff37373787ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01363636030e0e0e671616166b45454505323232432e2e2ed9151515c31d1d1d2dffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff014e4e4e05ffffff01ffffff01ffffff01ffffff010000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff28000000200000004000000001002000000000008010000000000000000000000000000000000000ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff017272721b646464a54646466f72727205ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff0168686845575757b74f4f4f39ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff017e7e7e0b6262627d616161f3636363ff424242ff444444d74f4f4f49ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff016c6c6c27636363b5616161ff555555ff434343ff464646a35858581dffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff01ffffff016666665d616161e3626262ff636363ff636363ff444444ff464646ff434343ff454545b95252522bffffff01ffffff01ffffff01ffffff016c6c6c1363636393616161fb636363ff636363ff555555ff464646ff464646ff444444f5464646836666660bffffff01ffffff01ffffff01ffffff01ffffff016a6a6a3f626262c9616161ff636363ff636363ff636363ff636363ff444444ff464646ff464646ff464646ff434343fb48484897545454135b5b5b036868686f616161ef626262ff636363ff636363ff636363ff555555ff464646ff464646ff464646ff454545ff444444e54a4a4a5fffffff01ffffff01ffffff01ffffff013b3b3bd7505050ff646464ff636363ff636363ff636363ff63 |