├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── build-deploy.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE-CC0.txt ├── README.md ├── book.toml ├── content ├── 3d_disp_cnt.md ├── 3d_geometry_engine.md ├── 3d_light_commands.md ├── 3d_matrix_commands.md ├── 3d_rendering_engine.md ├── 3d_status_registers.md ├── 3d_test_commands.md ├── 3d_vertex_polygon_commands.md ├── 3d_vital_commands.md ├── SUMMARY.md ├── bios.md ├── cpu_reference.md ├── ds_2d_graphics.md ├── ds_3d_graphics.md ├── ds_cartridges.md ├── ds_introduction.md ├── ds_io_map.md ├── ds_mem_control.md ├── ds_peripherals.md ├── ds_sound.md ├── ds_video.md ├── ds_wifi.md ├── ds_wifi_general_regs.md ├── ds_wifi_io_map.md ├── dsi_aes.md ├── dsi_cameras.md ├── dsi_control_regs.md ├── dsi_dsp_xpertteak.md ├── dsi_gpio.md ├── dsi_i2c_bus.md ├── dsi_introduction.md ├── dsi_io_map.md ├── dsi_ndma.md ├── dsi_sd_mmc.md ├── dsi_shared_wram.md ├── dsi_sound.md ├── dsi_touch_screen.md ├── dsi_wifi.md ├── fonts │ ├── Font_LICENSE │ ├── SourceSans3-It.otf.woff │ ├── SourceSans3-Regular.otf.woff │ ├── SourceSans3-Semibold.otf.woff │ ├── SourceSans3-SemiboldIt.otf.woff │ ├── fira_code.css │ ├── woff │ │ ├── FiraCode-Bold.woff │ │ ├── FiraCode-Light.woff │ │ ├── FiraCode-Medium.woff │ │ ├── FiraCode-Regular.woff │ │ ├── FiraCode-SemiBold.woff │ │ └── FiraCode-VF.woff │ └── woff2 │ │ ├── FiraCode-Bold.woff2 │ │ ├── FiraCode-Light.woff2 │ │ ├── FiraCode-Medium.woff2 │ │ ├── FiraCode-Regular.woff2 │ │ ├── FiraCode-SemiBold.woff2 │ │ └── FiraCode-VF.woff2 ├── foreword.md ├── other.md └── rom_header.md ├── mdbook-external-links ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src │ └── main.rs ├── mdbook-toc ├── .github │ └── workflows │ │ ├── deploy.yml │ │ └── tests.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── flake.lock ├── flake.nix ├── release.toml ├── src │ ├── bin │ │ └── mdbook-toc.rs │ └── lib.rs └── tests │ ├── adds_toc.in.md │ ├── adds_toc.out.md │ ├── attributes.in.md │ ├── attributes.out.md │ ├── backslash_escapes.in.md │ ├── backslash_escapes.out.md │ ├── crlf.in.md │ ├── crlf.out.md │ ├── empty_document.in.md │ ├── empty_document.out.md │ ├── github_marker.in.md │ ├── github_marker.out.md │ ├── gitlab_marker.in.md │ ├── gitlab_marker.out.md │ ├── handles_inline_code.in.md │ ├── handles_inline_code.out.md │ ├── higher_max_level.in.md │ ├── higher_max_level.out.md │ ├── it.rs │ ├── lower_max_level.in.md │ ├── lower_max_level.out.md │ ├── multi_header.in.md │ ├── multi_header.out.md │ ├── multi_header_linear.in.md │ ├── multi_header_linear.out.md │ ├── nonhtml_marker_no_use.in.md │ ├── nonhtml_marker_no_use.out.md │ ├── similar_heading_different_casing.in.md │ ├── similar_heading_different_casing.out.md │ ├── tables_untouched.in.md │ ├── tables_untouched.out.md │ ├── tables_with_html.in.md │ ├── tables_with_html.out.md │ ├── unique_slugs.in.md │ ├── unique_slugs.out.md │ ├── with_inline_code.in.md │ └── with_inline_code.out.md ├── mdbook-xnos └── mdbook-xnos.py ├── preproc ├── Cargo.toml └── src │ ├── admonitions.rs │ ├── git.rs │ └── main.rs └── theme ├── book.js ├── css ├── general.css └── tonc.css ├── head.hbs ├── index.hbs └── templates ├── archives.html ├── article.html ├── author.html ├── authors.html ├── base.html ├── categories.html ├── category.html ├── gosquared.html ├── index.html ├── page.html ├── pagination.html ├── period_archives.html ├── tag.html ├── tags.html └── translations.html /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | Cargo.lock -diff 3 | *.css text 4 | *.htm text 5 | *.html text 6 | *.md text 7 | *.jpg binary 8 | *.png binary 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: gbadev-org 2 | open_collective: gbadev 3 | -------------------------------------------------------------------------------- /.github/workflows/build-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy an updated version of ndsdoc 2 | 3 | on: 4 | push 5 | 6 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 7 | permissions: 8 | contents: read 9 | pages: write 10 | id-token: write 11 | 12 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 13 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: false 17 | 18 | jobs: 19 | build: 20 | name: Build ndsdoc 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout ndsdoc (and submodules) 24 | uses: actions/checkout@v2 25 | with: 26 | path: ndsdoc 27 | submodules: 'true' 28 | 29 | - name: Install mdbook 30 | uses: peaceiris/actions-mdbook@v1 31 | with: 32 | mdbook-version: 0.4.33 33 | 34 | - name: Install static-sitemap-cli 35 | run: npm install static-sitemap-cli 36 | 37 | - name: Cache build dir 38 | uses: actions/cache@v2 39 | with: 40 | path: ndsdoc/target/ 41 | key: ${{ runner.os }}-build-${{ hashFiles('ndsdoc/Cargo.lock') }} 42 | restore-keys: | 43 | ${{ runner.os }}-build- 44 | 45 | - name: Build 46 | working-directory: ndsdoc/ 47 | env: 48 | MDBOOK_BUILD__CREATE_MISSING: "false" # Prevent creating missing files in SUMMARY.md 49 | run: | 50 | mdbook build 51 | 52 | - name: Generate sitemap 53 | run: | 54 | cd ndsdoc/output 55 | npx sscli --no-clean --base https://ndsdoc.gbadev.net/ 56 | 57 | - name: Upload artifact 58 | uses: actions/upload-pages-artifact@v1 59 | with: 60 | path: ndsdoc/output 61 | 62 | deploy: 63 | name: Deploy to GitHub pages 64 | # Do not run this unless *pushing* to the default branch (`main`). 65 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 66 | environment: 67 | name: github-pages 68 | url: ${{ steps.deployment.outputs.page_url }} 69 | runs-on: ubuntu-latest 70 | needs: build 71 | steps: 72 | - name: Deploy to GitHub Pages 73 | id: deployment 74 | uses: actions/deploy-pages@v2 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /output/ 2 | target/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mdbook-external-links"] 2 | path = mdbook-external-links 3 | url = https://github.com/jonahgoldwastaken/mdbook-external-links 4 | [submodule "mdbook-toc"] 5 | path = mdbook-toc 6 | url = https://github.com/badboy/mdbook-toc 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["mdbook-external-links", "mdbook-toc", "preproc"] 4 | -------------------------------------------------------------------------------- /LICENSE-CC0.txt: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ndsdoc 2 | 3 | ## Setup 4 | 5 | You need Python, Rust and mdBook. 6 | 7 | ```sh 8 | # clone the repo 9 | git clone --recurse-submodules git@github.com:gbadev-org/ndsdoc.git 10 | cd ndsdoc 11 | 12 | cargo install mdbook 13 | 14 | # run the development server 15 | mdbook serve --open 16 | ``` 17 | 18 | The book will be live at http://localhost:3000 -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "ndsdoc" 3 | description = "ndsdoc" 4 | src = "content" 5 | language = "en" 6 | 7 | [build] 8 | build-dir = "output" 9 | create-missing = false 10 | 11 | [preprocessor.pandocs] 12 | command = "cargo run -p pandocs-preproc --locked --release --" 13 | 14 | [output.html] 15 | git-repository-url = "https://github.com/gbadev-org/ndsdoc" 16 | edit-url-template = "https://github.com/gbadev-org/ndsdoc/edit/master/{path}" 17 | site-url = "/" 18 | additional-css = [ 19 | "theme/css/tonc.css", 20 | # "theme/css/tonc_dark.css" 21 | ] 22 | 23 | [preprocessor.toc] 24 | command = "cargo run -p mdbook-toc --locked --release --" 25 | # Doesn't seem to work because of how markdown parses this as an unresolved shorthand link. 26 | # Default marker is 27 | # marker = "[TOC]" 28 | renderer = ["html"] 29 | max-level = 2 30 | 31 | [preprocessor.external-links] 32 | command = "cargo run -p mdbook-external-links --locked --release --" 33 | 34 | [preprocessor.xnos] 35 | command = "python3 -X utf8 ./mdbook-xnos/mdbook-xnos.py" 36 | -------------------------------------------------------------------------------- /content/3d_disp_cnt.md: -------------------------------------------------------------------------------- 1 | # 3D Display Control 2 | 3 | This chapter talks about the registers used to control the 3D display. 4 | 5 | 6 | ## DISP3DCNT: 3D display control (0x4000060, R/W) 7 | 8 | | Bit(s) | Description | 9 | |--------|---------------------------------------------------------| 10 | | 0 | Texture mapping enable (0=Disable, 1=Enable) 11 | | 1 | Shading polygon attribute (0=Toon Shading, 1=Highlight Shading) 12 | | 2 | Alpha test (0=Disable, 1=Enable) (see [ALPHA\_TEST\_REF](3d_disp_cnt.md#ALPHA_TEST_REF)) 13 | | 3 | Alpha blending (0=Disable, 1=Enable) 14 | | 4 | Anti-aliasing (0=Disable, 1=Enable) 15 | | 5 | Edge marking (0=Disable, 1=Enable) (see `EDGE_COLOR`) 16 | | 6 | Fog color alpha mode (0=Alpha and color, 1=Only alpha) (see `FOG_COLOR`) 17 | | 7 | Fog enable (0=Disable, 1=Enable) 18 | | 8-11 | Fog depth shift (`FOG_STEP = 0x400 >> FOG_SHIFT`) (see `FOG_OFFSET`) 19 | | 12 | Framebuffer RDLINES underflow (0=None, 1=Underflow) 20 | | 13 | Polygon/vertex RAM overflow (0=None, 1=Overflow) 21 | | 14 | Rear plane mode (0=Use clear color, 1=Bitmap) 22 | | 15-31 | Unused 23 | 24 | To fill the screen: X1=0, Y1=0, X2=255, Y2=191 25 | 26 | Note that coordinate (0, 0) is the bottom-left corner of the screen, while ht is 27 | the upper-left corner of the screen in the 2D graphics engine. 28 | 29 | 30 | ## DISP\_1DOT\_DEPTH: 1-dot polygon render depth (0x4000610, W) 31 | 32 | When a polygon is too small or too far away it is reduced to a single pixel on 33 | the screen. This register will determine the cutoff distance that the 3D engine 34 | will use to determine whether to render them or not. 35 | 36 | | Bit(s) | Description | 37 | |--------|---------------------------------------------------------| 38 | | 0-14 | Max W value (12.3 unsigned fixed point) 39 | | 15-31 | Unused 40 | 41 | This check can be enabled on a per-polygon basis with bit 13 of `POLYGON_ATTR`. 42 | 43 | The comparison always uses the W coordinate regardless of the buffering mode. 44 | 45 | 46 | ## ALPHA\_TEST\_REF: Alpha test reference (0x4000340, W) 47 | 48 | When alpha test mode is enabled in [DISP3DCNT](3d_disp_cnt.md#DISP3DCNT), 49 | pixels will only be rendered if their alpha value is greater than the value in 50 | `ALPHA_TEST_REF`. Normally, when it is disabled, all pixels are rendered if 51 | their alpha value is greater than zero. A value of 31 will hide all polygons. 52 | 53 | This test is done after applying texture and polygon transparency. 54 | 55 | | Bit(s) | Description | 56 | |--------|---------------------------------------------------------| 57 | | 0-4 | Alpha test reference (0...31) 58 | | 5-31 | Unused 59 | -------------------------------------------------------------------------------- /content/3d_geometry_engine.md: -------------------------------------------------------------------------------- 1 | # 3D Geometry Engine 2 | 3 | The Geometry Engine on the Nintendo DS is the hardware responsible for taking in 3D Graphics Commands and coverting them to verticies and polygons which can be rendered by the Rendering Engine. It's jobs include performing various matrix multiplications involving vertex positions, vertex colors, light vectors, etc. 4 | 5 | # Readable Matricies 6 | 7 | The Geometry provides access to the final directional matrix and screen space transformation matrix. (as in, position matricies * projection matrix) Reading these requires the geometry engine to be disabled via bit 27 in the ``GXSTAT`` register. 8 | 9 | ## Clip Matrix (0x4000640..=0x400067F, 16 words, R) 10 | 11 | Read the 4x4 "Clip matrix" with cells going from row 0 column 0, row by row. 12 | 13 | ## Read Directional Matrix (0x4000680..=0x40006A3, 9 words, R) 14 | 15 | Read the "top left" 3x3 segment of the directional matrix with cells going from row 0 column 0, row by row. 16 | 17 | # 3D Command FIFO 18 | The Geometry engine is heavily based upon a Command FIFO and PIPE used to transfer vertex lists, bind textures, set viewport, modify matricies, etc. The FIFO can be directly accessed by writing command numbers and parameters to memory location ``0x4000400`` or indirectly by writing parameters to the corresponding "Command Ports" at various memory locations from``0x4000440..=0x40005FF``. 19 | 20 | ## Using the FIFO indirectly 21 | The simpler and more user friendly method of interacting with the FIFO is via the "Command Ports", where parameters are written to the corresponding memory address for a given command. The command + parameter combo is automatically sent down the FIFO to the 3D hardware once every parameter has been sent. For commands that don't have any parameters one may simply write any value to the port. 22 | 23 | ## Using the FIFO directly 24 | When using the FIFO directly, commands are sent by first writing a "Command word" containing up to 4 packed command indicies. Followed by writing the parameters for each command in order. If the last command does not have a parameter, 0 must be written as a dummy parameter in order for the hardware to accept a new "command word". When trying to specify invalid command indicies they will be treated the same as command index 0. (no command, no parameters) This way of using the fifo is better suited to transferring large chunks of commands, such as via DMA. 25 | 26 | ### "Command word" definition 27 | 28 | | Bit(s) | Description | 29 | |--------|---------------------------------------------------------| 30 | | 0-7 | Command Index 0 31 | | 8-15 | Command Index 1 32 | | 16-23 | Command Index 2 33 | | 24-31 | Command Index 3 34 | 35 | #### Notes: 36 | When packing multiple commands you may not leave zeroed indicies (indicating no command) in between non-zeroed indicies. Meaning that when sending one command the top 24 bits must be zero, when sending 2 commands the top 16 bits must be zero, and when sending 3 commands the top 8 bits must be zero. 37 | 38 | ## Command Summary 39 | 40 | | Command Index | Port address | Summary | 41 | |---------------|--------------|--------------------------------------| 42 | | 0x10 | 0x0400_0440 | Select Matrix Mode/Matrix Stack 43 | | 0x11 | 0x0400_0444 | Push matrix onto stack 44 | | 0x12 | 0x0400_0448 | Pull matrix from stack 45 | | 0x13 | 0x0400_044C | Store matrix on stack 46 | | 0x14 | 0x0400_0450 | Restore matrix from stack 47 | | 0x15 | 0x0400_0454 | Set current matrix to ``I`` (Unit/Identity matrix) 48 | | 0x16 | 0x0400_0458 | Load 4x4 values into current matrix. 49 | | 0x17 | 0x0400_045C | Load 4x3 values into current matrix. 50 | | 0x18 | 0x0400_0460 | Multiply current matrix by 4x4 Matrix 51 | | 0x19 | 0x0400_0464 | Multiply current matrix by 4x3 Matrix 52 | | 0x1A | 0x0400_0468 | Multiply current matrix by 3x3 Matrix 53 | | 0x1B | 0x0400_046C | Scale current matrix by vector 54 | | 0x1C | 0x0400_0470 | Translate current matrix by vector 55 | | 0x20 | 0x0400_0480 | Set Vertex Color 56 | | 0x21 | 0x0400_0484 | Set Vertex Normal 57 | | 0x22 | 0x0400_0488 | Set Vertex Texture Coordinates 58 | | 0x23 | 0x0400_048C | Set Vertex Position (16-bit) 59 | | 0x24 | 0x0400_0490 | Set Vertex Position (10-bit) 60 | | 0x25 | 0x0400_0494 | Set Vertex XY position 61 | | 0x26 | 0x0400_0498 | Set Vertex XZ position 62 | | 0x27 | 0x0400_049C | Set Vertex YZ position 63 | | 0x28 | 0x0400_04A0 | Set Vertex position relative to the last 64 | | 0x29 | 0x0400_04A4 | Set Vertex Attributes 65 | | 0x2A | 0x0400_04A8 | Set Texture Parameters 66 | | 0x2B | 0x0400_04AC | Set Texture Palette 67 | | 0x30 | 0x0400_04C0 | Set Diffused/Ambient Color 68 | | 0x31 | 0x0400_04C4 | Set Specular/Emissive Color 69 | | 0x32 | 0x0400_04C8 | Set Light Direction 70 | | 0x33 | 0x0400_04CC | Set Light Color 71 | | 0x34 | 0x0400_04D0 | Set Shininess table 72 | | 0x40 | 0x0400_0500 | Start vertex list 73 | | 0x41 | 0x0400_0504 | End vertex list 74 | | 0x50 | 0x0400_0540 | Swap Buffers between geometry and rendering engine 75 | | 0x60 | 0x0400_0580 | Set 3D Viewport 76 | | 0x70 | 0x0400_05C0 | Test view volume against cuboid 77 | | 0x71 | 0x0400_05C4 | Test Position Vector 78 | | 0x72 | 0x0400_05C8 | Test Direction Vector 79 | 80 | -------------------------------------------------------------------------------- /content/3d_light_commands.md: -------------------------------------------------------------------------------- 1 | # 3D Light Commands 2 | 3 | These are the commands which specifies the lighting conditions for any upcoming polygons and verticies. 4 | 5 | 6 | ## Set Lights Directional Vector: Port 0x040004C8, Index 0x32, 1 Parameter 7 | 8 | Sets the direction a given light points in. 9 | 10 | Parameter Definition: 11 | 12 | | Bit(s) | Description | 13 | |--------|-------------| 14 | | 0-9 | X coordinate 15 | | 10-19 | Y coordinate 16 | | 20-29 | Z coordinate 17 | | 30-31 | Light Index (0..=3) 18 | 19 | all coordinate parameters are in the same format, as in: 1bit sign + 9 bit fraction. 20 | 21 | 22 | ## Set Lights Color: Port 0x040004CC, Index 0x33, 1 Parameter 23 | 24 | Sets the color of a given light. 25 | 26 | Parameter Definition: 27 | 28 | | Bit(s) | Description | 29 | |--------|-------------| 30 | | 0-4 | Red 31 | | 5-9 | Green 32 | | 10-14 | Blue 33 | | 15-29 | Unused 34 | | 30-31 | Light Index (0..=3) 35 | 36 | 37 | ## Set Shininess table: Port 0x040004D0, Index 0x34, 32 Parameters 38 | 39 | Sets the contents of a 128-byte shininess table used for specular reflections. transferred 4 entries (bytes) at a time. 0 = least shiny, 255 = most shiny. 40 | 41 | Notes: When the shininess table is disabled, the Rendering Engine will act as if the table is filled with linearly increasing entries from the minimum to the maximum. -------------------------------------------------------------------------------- /content/3d_matrix_commands.md: -------------------------------------------------------------------------------- 1 | # 3D Matrix Commands 2 | On the DS there are multiple matrix operations as well as matrix stacks responsible for calculating the resulting light, normal, and position vectors related to any given vertex. These matrix stacks are: 3 | 4 | * Projection stack (Size: 1, Mode: 0) 5 | * Coordinate stack (Size: 32, Mode: 1,2) 6 | * Directional stack (Size: 32, Mode: 1,2) 7 | * Texture stack: (Size: 1, Mode: 3) 8 | 9 | Mode in this case reffers to what index needs to be sent to CMD 0x10 in order to "select" the given stack. Notice how the coordinate and directional stack share modes 1 and 2. Which indicates how both are changed when in those modes (and also internally share the same stack pointer). The two modes however change both stacks in different ways depending on the commands used. 10 | 11 | When working with the matrix commands, you may often stumble upon the term "Current matrix", which reffers to the matrix at the top of the selected matrix stack(s). 12 | 13 | # Matrix Stack Commands 14 | 15 | Here are the commands which depends on the matrix stack(s): 16 | 17 | 18 | ## Set Matrix Mode: Port 0x04000440, Index 0x10, 1 Parameter 19 | Selects the matrix stack which is to be modified 20 | 21 | Parameter Definition: 22 | 23 | | Bit(s) | Description | 24 | |--------|---------------------------------------------------------| 25 | | 0-1 | Mode Index 26 | | 2-31 | Unused 27 | 28 | Possible Mode Indicies: 29 | 30 | | Mode Index | Selected Stack and purpose | 31 | |------------|----------------------------| 32 | | 0 | Selects Projection Matrix, in a traditional MVP matrix this would be the P part. 33 | | 1 | Position and Direction Matrix stack. In a traditional MVP matrix this would be the MV parts. 34 | | 2 | Position and Direction Matrix stack. Primarily used in lighting and test related context. 35 | | 3 | Selects Texture Coordinate Matrix stack. Modifies how texture coordinates are transformed. 36 | 37 | 38 | 39 | ## Push Matrix stack: Port 0x04000444, Index 0x11, No Parameter 40 | Pushes the "Current matrix" onto the stack 41 | 42 | 43 | ## Pull Matrix stack: Port 0x04000448, Index 0x12, 1 Parameter 44 | Pops N matricies off the current matrix stack 45 | 46 | Parameter Definition: 47 | 48 | | Bit(s) | Description | 49 | |--------|---------------------------------------------------------| 50 | | 0-5 | Ammount of matricies to pop in range -30..=31 (N) 51 | | 6-31 | Unused 52 | 53 | Notes: 54 | 55 | On matrix stacks with a size of 1, the parameter is ignored and 1 is always used. 56 | 57 | 58 | ## Store Current Matrix on Stack: Port 0x0400044C, Index 0x13, 1 Parameter 59 | Stores the current matrix on another part of the stack. Leaves the stack pointer unchanged. 60 | 61 | Parameter Definition: 62 | 63 | | Bit(s) | Description | 64 | |--------|---------------------------------------------------------| 65 | | 0-4 | Stack Offset 66 | | 5-31 | Unused 67 | 68 | Note: 69 | 70 | A Stack Offset of 31 causes the stack error flag in the ``GXSTAT`` register to be set. 71 | 72 | On stacks where the size is 1, the parameter is ignored and 0 is always used. 73 | 74 | 75 | ## Restore Current Matrix from Stack: Port 0x04000450, Index 0x14, 1 Parameter 76 | Sets the current matrix to the values of another matrix on the stack. Leaves the stack pointer unchanged. 77 | 78 | Parameter Definition: 79 | 80 | | Bit(s) | Description | 81 | |--------|---------------------------------------------------------| 82 | | 0-4 | Stack Offset 83 | | 5-31 | Unused 84 | 85 | Note: 86 | 87 | A Stack Offset of 31 causes the stack error flag in the ``GXSTAT`` register to be set. 88 | 89 | On stacks where the size is 1, the parameter is ignored and 0 is always used. 90 | 91 | # General Matrix Commands 92 | 93 | These are the commands which modify the current matrix: 94 | 95 | 96 | ## Load Identity Matrix: Port 0x04000454, Index 0x15, No Parameter 97 | Sets the current matrix to a Unit/Identity matrix. Sometimes denoted as ``I`` or ``1``. 98 | 99 | 100 | ## Load 4x4 Matrix: Port 0x04000458, Index 0x16, 16 Parameters 101 | Sets the current matrix to a 4x4 matrix, where each parameter corresponds to a cell in the matrix. Values are loaded row by row. starting at row 0 column 0. 102 | 103 | 104 | ## Load 4x3 Matrix: Port 0x0400045C, Index 0x17, 12 Parameters 105 | Sets the current matrix to a 4x3 matrix (padded to a 4x4 matrix which doesn't scale W), where each parameter corresponds to a cell in the matrix. Values are loaded row by row. starting at row 0 column 0. 106 | 107 | 108 | ## Multiply 4x4 Matrix: Port 0x04000460, Index 0x18, 16 Parameters 109 | Multiply the current matrix by a 4x4 matrix, where each parameter corresponds to a cell in the matrix. Values are loaded row by row. starting at row 0 column 0. 110 | 111 | 112 | ## Multiply 4x3 Matrix: Port 0x04000464, Index 0x19, 12 Parameters 113 | Multiply the current matrix by a 4x3 matrix (padded to a 4x4 matrix which doesn't scale W), where each parameter corresponds to a cell in the matrix. Values are loaded row by row. starting at row 0 column 0. 114 | 115 | 116 | ## Multiply 3x3 Matrix: Port 0x04000468, Index 0x1A, 9 Parameters 117 | Multiply the current matrix by a 3x3 matrix (padded to a 4x4 matrix which makes W unnaffected), where each parameter corresponds to a cell in the matrix. Values are loaded row by row. starting at row 0 column 0. 118 | 119 | 120 | ## Scale Current Matrix by vector: Port 0x0400046C, Index 0x1B, 3 Parameters 121 | Multiply the current matrix by a scale matrix where each scalar value corresponds to the coordinates in a vector. (W coordinate is always 1) 122 | 123 | 124 | ## Translate Current Matrix by vector: Port 0x04000470, Index 0x1C, 3 Parameters 125 | Multiply the current matrix by a translation matrix containing the supplied vector coordinates. 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /content/3d_rendering_engine.md: -------------------------------------------------------------------------------- 1 | # 3D Rendering Engine 2 | 3 | The Rendering Engine on the Nintendo DS is responsible for taking in the Vertex And Polygon buffers from the Geometry Engine and constructing frames of video. This can either be displayed as background 0 on the main 2D PPU or captured by the capture unit to be used as a bitmap image. 4 | 5 | The Rendering Engine uses a 48-line "Scanline Cache" as opposed to a full framebuffer containing the entire output of a frame when rendering. -------------------------------------------------------------------------------- /content/3d_status_registers.md: -------------------------------------------------------------------------------- 1 | # 3D Display Control 2 | 3 | This chapter talks about the status registers of the 3D hardware. 4 | 5 | 6 | ## GXSTAT: Geometry Engine Status Register (0x04000600, mixed R/W) 7 | 8 | | Bit(s) | Description | 9 | |--------|--------------------| 10 | | 0 | Test functions Busy 11 | | 1 | Box Test Result 12 | | 2-7 | unused 13 | | 8-12 | Position & Directional Matrix Stack Pointer 14 | | 13 | Projection Matrix Stack Pointer 15 | | 14 | Matrix Stack Busy 16 | | 15 | Matrix Stack Overflow/Undeflow 17 | | 16-24 | Number of 40-bit entries in Command FIFO 18 | | 25 | FIFO is less than half full 19 | | 26 | FIFO is empty 20 | | 27 | General Busy Flag 21 | | 28-29 | unused 22 | | 30-31 | Command FIFO IRQ (0 = Never, 1 = Less than half full, 2 = Empty, 3 = Reserved) 23 | 24 | 25 | ## RAM statistics: Polygon List & Vertex RAM Count (0x04000604, R) 26 | 27 | | Bit(s) | Description | 28 | |--------|--------------------| 29 | | 0-11 | Number of Polygons currently in Polygon List RAM 30 | | 12-15 | unused 31 | | 16-28 | Number of Verticies currently in Vertex RAM 32 | | 29-31 | unused 33 | 34 | Note: Once a buffer swap has been sent, the counters are reset 10 cycles after V-Blank. 35 | 36 | ## Performance Statistics (0x04000320, R) 37 | 38 | | Bit(s) | Description | 39 | |--------|--------------------| 40 | | 0-5 | Minimum number of buffered lines during last frame - 2 41 | | 6-31 | unused 42 | 43 | -------------------------------------------------------------------------------- /content/3d_test_commands.md: -------------------------------------------------------------------------------- 1 | # 3D Test Commands 2 | 3 | These Commands test various parameters against desired results by the user. Each command requires that the "test busy" bit of the ``GXSTAT`` register is cleared before reading the result. 4 | 5 | 6 | ## Test if Cuboid Intersects View Volume: Port 0x040005C0, Index 0x70, 3 Parameters 7 | 8 | The result of this command can be read from bit 1 of the ``GXSTAT`` register and indicates if any part of the specified cuboid intesects the view volume. A value of 1 indicates that the cuboid would have been visible if drawn. 9 | 10 | Parameter definition: 11 | 12 | | Parameter | Bit(s) | Description | 13 | |-----------|--------|-------------| 14 | | 1 | 0-15 | Origin X coordinate 15 | | 1 | 16-31 | Origin Y coordinate 16 | | 2 | 0-15 | Origin Z coordinate 17 | | 2 | 16-31 | Width (X-Offset) 18 | | 3 | 0-15 | Height (Y-Offset) 19 | | 3 | 16-31 | Depth (Z-Offset) 20 | 21 | all parameters are in the same format as 16 bit vertex coordinates. As in, 1bit sign + 3 bit integer + 12 bit fraction. 22 | 23 | 24 | ## Set Coordinates for Position Test: Port 0x040005C4, Index 0x71, 2 Parameters 25 | 26 | Takes the vector ``(x,y,z,1)`` and multiplies by the positional and projection matrix stacks. Result can be read from the memory region at ``0x04000620..=0x0400062F`` where each word corresponds to a coordinate of the resulting vector. In format 1bit sign + 19 bit integer + 12 bit fraction. 27 | 28 | Parameter Definition: 29 | 30 | | Parameter | Bit(s) | Description | 31 | |-----------|--------|-------------| 32 | | 1 | 0-15 | X coordinate 33 | | 1 | 16-31 | Y coordinate 34 | | 2 | 0-15 | Z coordinate 35 | | 2 | 16-31 | Unused 36 | 37 | all parameters are in the same format as 16 bit vertex coordinates. As in, 1bit sign + 3 bit integer + 12 bit fraction. 38 | 39 | Notes: This command should not be issued while a vertex list is being constructed. As any vertex position commands that inherit the coordinates of the previous vertex will instead inherit the coordinates set by this command. 40 | 41 | 42 | ## Set Directional vector for Direction Test: Port 0x040005C8, Index 0x72, 1 Parameter 43 | 44 | Takes the vector ``(x,y,z,0)`` and multiplies by the directional matrix stack. (according to no$ also requires matrix mode 2?) Result can be read from the memory region at ``0x04000630..=0x04000635`` where each 2byte half word corresponds to a coordinate of the resulting vector. In format 4bit sign + 12 bit fraction. (sign is either ``0b0000`` or ``0b1111``) 45 | 46 | Parameter Definition: 47 | 48 | | Bit(s) | Description | 49 | |--------|-------------| 50 | | 0-9 | X coordinate 51 | | 10-19 | Y coordinate 52 | | 20-29 | Z coordinate 53 | | 30-31 | Unused 54 | 55 | all parameters are in the same format as those for other light direction commands, as in: 1bit sign + 9 bit fraction. -------------------------------------------------------------------------------- /content/3d_vertex_polygon_commands.md: -------------------------------------------------------------------------------- 1 | # Vertex and Polygon Commands 2 | 3 | This chapter explains the various commands used to send verticies to the 3D hardware in order to construct polygons. On top of this theres also basic lighting, materials, and texture mapping attributes. The DS 3D hardware natively supports constructing both triangles and quadrilaterals (which should not self intersect or have concavities). 4 | 5 | ## Usage Overview 6 | Verticies, as well as edges and polygons are to be constructed on the 3D hardware in the following manner: 7 | 8 | 1. Set Polygon attributes 9 | 10 | 2. Start a vertex list, where the parameter passed to the start command dictates the type of polygons you wish to construct (Seperated or Connected, Tris or Quads) 11 | 12 | 3. For each vertex, set relevant vertex attributes in any order. Setting any coordinate of the vertex position sends it to the 3D hardware. Attributes that are left unset will use the values of the previous vertex. 13 | 14 | 4. End the vertex list. 15 | 16 | Notice here that the 3D hardware does not natively support index lists for constructing polygons, 17 | only a vertex list that constructs either seperated or connected "strips" of primitives. Another 18 | quirk of the 3D hardware is that the last step is optional, as vertex lists are automatically 19 | ended when a new one begins or the geometry and rendering engine buffers are swapped. It may 20 | still be useful however to manually end it for debugging purposes and the sake of "Completeness". 21 | 22 | 23 | 24 | 25 | 26 | 27 | # Vertex List Commands 28 | Commands used to start and end vertex lists, as well as commands which can only be used once per vertex list. 29 | 30 | 31 | ## Start Vertex List: Port 0x04000500, Index 0x40, 1 Parameter 32 | Parameter definition: 33 | 34 | | Bit(s) | Description | 35 | |--------|-------------| 36 | | 0 | Primitive Type (0 = triangle, 1 = quadrilateral) 37 | | 1 | Primitive Topology (0 = seperated, 1 = strips) 38 | 39 | 40 | ## End Vertex List: Port 0x04000504, Index 0x41, 0 Parameters 41 | Ends a vertex list, as previously discussed this command is optional, and only require for the sake of debugging 42 | 43 | 44 | ## Polygon Attributes: Port 0x040004A4, Index 0x29, 1 Parameter 45 | Sets various miscallenous attributes for polygons created by the next vertex list. 46 | 47 | Parameter definition: 48 | 49 | | Bit(s) | Description | 50 | |--------|-------------| 51 | | 0 | Enable Light 0 52 | | 1 | Enable Light 1 53 | | 2 | Enable Light 2 54 | | 3 | Enable Light 3 55 | | 4-5 | Polygon Mode (0 = Modulation, 1=Decal, 2=Toon, 3=Shadow) 56 | | 6 | Render Backface 57 | | 7 | Render Frontface 58 | | 8-10 | Unused 59 | | 11 | Set new depth for translucent pixels 60 | | 12 | Render Far-Plane intersecting polygons 61 | | 13 | Render 1-Dot polygons behind 1-Dot depth 62 | | 14 | Depth test (0 = less than, 1 = equal) 63 | | 15 | Enable Fog 64 | | 16-20 | Alpha (0 = Wireframe, 1..30 = Translucent, 31 = Solid) 65 | | 21-23 | Unused 66 | | 24-29 | Polygon ID 67 | | 30-31 | Unused 68 | 69 | Notes: If a polygon attribute command is sent while a vertex list is ongoing, it will be deffered until next time a vertex list is begun. (Repeated calls will not stack, only the last written one will be used) Changes in lighting related attributes will not change until a new normal vector is set/calculated. 70 | 71 | 72 | 73 | 74 | 75 | # Vertex Attribute Commands 76 | These are the commands for various attributes which can be applied to a vertex. 77 | 78 | 79 | ## Set Vertex Color: Port 0x04000480, Index 0x20, 1 Parameter 80 | Sets the vertex color used for the next vertex. 81 | 82 | Parameter definition: 83 | 84 | | Bit(s) | Description | 85 | |--------|-------------| 86 | | 0-4 | Red 87 | | 5-9 | Green 88 | | 10-14 | Blue 89 | | 15-31 | Unused 90 | 91 | 92 | 93 | ## Set Normal Vector: Port 0x04000484, Index 0x21, 1 Parameter 94 | Sets the "normal vector" used for the next vertex. In reality this will use the currently set light parameters to calculate a new vertex color. 95 | 96 | Parameter definition: 97 | 98 | | Bit(s) | Description | 99 | |--------|-------------| 100 | | 0-9 | Vertex X coordinate (sign + 9 bit fraction) 101 | | 10-19 | Vertex Y coordinate (sign + 9 bit fraction) 102 | | 20-29 | Vertex Z coordinate (sign + 9 bit fraction) 103 | | 30-31 | Unused 104 | 105 | 106 | ## Set Texture Coordinates: Port 0x04000488, Index 0x22, 1 Parameter 107 | Sets the UV / ST coordinates for the next vertex. 16 units = 1 texel 108 | 109 | Parameter definition: 110 | 111 | | Bit(s) | Description | 112 | |--------|-------------| 113 | | 0-15 | S/U coordinate (signed, 4 bit fraction) 114 | | 16-31 | T/V coordinate (signed, 4 bit fraction) 115 | 116 | 117 | ## Set Texture Parameters: Port 0x040004A8, Index 0x2A, 1 Parameter 118 | Binds a texture to the upcoming verticies, along with various texture attributes. 119 | 120 | Parameter definition: 121 | 122 | | Bit(s) | Description | 123 | |--------|-------------| 124 | | 0-15 | Texture VRAM offset (in steps of 8) 125 | | 16 | Repeat Horizontally (0 = Clamp, 1 = Repeat) 126 | | 17 | Repeat Vertically (0 = Clamp, 1 = Repeat) 127 | | 18 | Flip on horizontal repeat (see notes.) 128 | | 19 | Flip on Vertical repeat (see notes.) 129 | | 20-22 | Horizontal size (see notes.) 130 | | 23-25 | Vertical size (see notes.) 131 | | 26-28 | Texture Format (see notes.) 132 | | 29 | Set color 0 to transparent 133 | | 30-31 | Texture Coordinate transformation mode (see notes.) 134 | 135 | Notes: 136 | 137 | Texture size is calculated as (8 << N) pixels where N is the specified size. Meaning possible resulting values are 8,16,32,64,128,256,512,1024. 138 | 139 | Texture flipping on repeats requires that repeats are on. 140 | 141 | When a Texture is clamped, the outmost edge pixels are stretched across any out of bounds pixels. 142 | 143 | The various Texture Formats are as follows: 144 | 145 | | Index | Description | 146 | |-------|-------------| 147 | | 0 | No texture 148 | | 1 | A315 Translucent Texture 149 | | 2 | 4-Color Paletted texture 150 | | 3 | 16-Color Paletted texture 151 | | 4 | 256-Color Paletted texture 152 | | 5 | Compressed Texture 153 | | 6 | A513 Translucent texture; 154 | | 7 | Bitmap Texture (R5G5B5) 155 | 156 | Texture Coordinate Transformation Modes: 157 | 158 | | Index | Description | 159 | |-------|-------------| 160 | | 0 | No transformation 161 | | 1 | Texture Coordinate Source 162 | | 2 | Normal Source 163 | | 3 | Vertex Source 164 | 165 | 166 | 167 | ## Set Texture Color Palette: Port 0x040004AC, Index 0x2B, 1 Parameter 168 | Set the color palette used for the currently binded texture. Ignored by non-palette based textures. 169 | 170 | Parameter Definition: 171 | 172 | | Bit(s) | Description | 173 | |--------|-------------| 174 | | 0-12 | Texture Palette VRAM offset (see notes.) 175 | | 13-31 | Ignored 176 | 177 | Notes: When using 4-color paletted textures the offset is calculated in steps of 8. In any other cases it's steps of 16. 178 | 179 | 180 | ## Diffuse/Ambient Properties: Port 0x040004C0, index 0x30, 1 Parameter 181 | Sets the Diffused/Ambient properties for the next vertex. 182 | 183 | Parameter definition: 184 | 185 | | Bit(s) | Description | 186 | |--------|-------------| 187 | | 0-4 | Diffuse Reflection (Red channel) 188 | | 5-9 | Diffuse Reflection (Green channel) 189 | | 10-14 | Diffuse Refletcion (Blue channel) 190 | | 15 | Set Diffuse reflection as vertex color (0 = No, 1 = Yes) 191 | | 16-20 | Ambient Reflection (Red Channel) 192 | | 21-25 | Ambient Reflection (Green channel) 193 | | 26-30 | Ambient Reflection (Blue channel) 194 | | 31 | Unused 195 | 196 | 197 | ## Specular/Emissive Properties: Port 0x040004C4, index 0x31, 1 Parameter 198 | Sets the Specular/Emissive properties for the next vertex. 199 | 200 | Parameter definition: 201 | 202 | | Bit(s) | Description | 203 | |--------|-------------| 204 | | 0-4 | Specular Reflection (Red channel) 205 | | 5-9 | Specular Reflection (Green channel) 206 | | 10-14 | Specular Refletcion (Blue channel) 207 | | 15 | Use shininess table (0 = No, 1 = Yes, see chapter on other commands for shininess table details) 208 | | 16-20 | Emission (Red Channel) 209 | | 21-25 | Emission (Green channel) 210 | | 26-30 | Emission (Blue channel) 211 | | 31 | Unused 212 | 213 | 214 | 215 | 216 | 217 | # Vertex Position Commands 218 | All of these commands send a new vertex to the geometry engine once executes. 219 | Unless stated otherwise, the vertex coordinates will be a 16bit signed integer 220 | with a 12bit fraction. (i.e Range is +7.99 to -8.00) 221 | 222 | 223 | ## Position Set (16-bit): Port 0x0400048C, Index 0x23, 2 Parameters 224 | Set all coordinates of the next vertex at once. 225 | 226 | Parameter Definition: 227 | 228 | | Parameter | Bit(s) | Description | 229 | |-----------|--------|-------------| 230 | | 1 | 0-15 | Vertex X coordinate 231 | | 1 | 16-31 | Vertex Y coordinate 232 | | 2 | 0-15 | Vertex Z coordinate 233 | | 2 | 16-31 | Unused 234 | 235 | 236 | ## Position Set (10-bit): Port 0x04000490, Index 0x24, 1 Parameter 237 | Set all coordinates of the next vertex at once, but with less accuracy. 238 | 239 | Parameter Definition: 240 | 241 | | Bit(s) | Description | 242 | |--------|-------------| 243 | | 0-9 | Vertex X coordinate (signed, 6 bit fraction) 244 | | 10-19 | Vertex Y coordinate (signed, 6 bit fraction) 245 | | 20-29 | Vertex Z coordinate (signed, 6 bit fraction) 246 | | 30-31 | Unused 247 | 248 | 249 | ## Position Set XY: Port 0x04000494, Index 0x25, 1 Parameter 250 | Set X and Y coordinate of the next vertex. 251 | 252 | Parameter Definition: 253 | 254 | | Bit(s) | Description | 255 | |--------|-------------| 256 | | 0-15 | Vertex X coordinate 257 | | 16-31 | Vertex Y coordinate 258 | 259 | 260 | ## Position Set XZ: Port 0x04000498, Index 0x26, 1 Parameter 261 | Set X and Z coordinate of the next vertex. 262 | 263 | Parameter Definition: 264 | 265 | | Bit(s) | Description | 266 | |--------|-------------| 267 | | 0-15 | Vertex X coordinate 268 | | 16-31 | Vertex Z coordinate 269 | 270 | 271 | ## Position Set YZ: Port 0x0400049C, Index 0x27, 1 Parameter 272 | Set Y and Z coordinate of the next vertex. 273 | 274 | Parameter Definition: 275 | 276 | | Bit(s) | Description | 277 | |--------|-------------| 278 | | 0-15 | Vertex Y coordinate 279 | | 16-31 | Vertex Z coordinate 280 | 281 | 282 | ## Position Set Relative: Port 0x040004A0, Index 0x28, 1 Parameter 283 | Set all coordinates of the next vertex at once, but relative to the last vertex. 284 | 285 | Parameter Definition: 286 | 287 | | Bit(s) | Description | 288 | |--------|-------------| 289 | | 0-9 | Vertex X coordinate (signed, 12 bit fraction, see notes.) 290 | | 10-19 | Vertex Y coordinate (signed, 12 bit fraction, see notes.) 291 | | 20-29 | Vertex Z coordinate (signed, 12 bit fraction, see notes.) 292 | | 30-31 | Unused 293 | 294 | Note: As the value is smaller than the fraction, the relative range is very small (+-0.125) 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | -------------------------------------------------------------------------------- /content/3d_vital_commands.md: -------------------------------------------------------------------------------- 1 | # Vital 3D Hardware Commands 2 | This chapter will go over the various commands required to be able to display graphics on the 3D hardware. 3 | 4 | 5 | ## Swap buffers: Port 0x4000540, Index 0x50, 1 Parameter 6 | 7 | Swaps the buffers used between the rendering engine, and the geometry engine. 8 | 9 | | Bit(s) | Description | 10 | |--------|---------------------------------------------------------| 11 | | 0 | Y Sorting for translucent polygons (0=Automatic, 1=Manual) 12 | | 1 | Depth buffering (0 = using Z value, 1 = using W value) 13 | | 2-31 | unused 14 | 15 | 16 | 17 | ## Set 3D viewport: Port 0x4000580, Index 0x60, 1 Parameter 18 | 19 | Sets the region of the screen which the 3D hardware may render to. 20 | 21 | | Bit(s) | Description | 22 | |--------|---------------------------------------------------------| 23 | | 0-7 | X1: Left-most X coordinate (0...255) 24 | | 8-15 | Y1: Bottom-most Y coordinate (0...191) 25 | | 16-23 | X2: Right-most X coordinate (0...255) 26 | | 24-31 | Y2: Top-most Y coordinate (0...191) 27 | 28 | Notes: 29 | 30 | Due to a misimplementation in the hardware, polygons can still be rendered one pixel beyond (X2, Y1) 31 | 32 | Setting the viewport should be reserved to only be done once per frame or once before the beginning of a vertex list. 33 | 34 | -------------------------------------------------------------------------------- /content/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Foreword](foreword.md) 4 | * [DS Introduction](ds_introduction.md) 5 | * [DS I/O Map](ds_io_map.md) 6 | * [DS Memory Control](ds_mem_control.md) 7 | * [DS Video](ds_video.md) 8 | * [DS 2D Graphics](ds_2d_graphics.md) 9 | * [DS 3D Graphics](ds_3d_graphics.md) 10 | * [3D Display Control](3d_disp_cnt.md) 11 | * [3D Status Registers](3d_status_registers.md) 12 | * [3D Geometry Engine](3d_geometry_engine.md) 13 | * [Vital Commands](3d_vital_commands.md) 14 | * [Vertex Commands](3d_vertex_polygon_commands.md) 15 | * [Matrix Commands](3d_matrix_commands.md) 16 | * [Light Commands](3d_light_commands.md) 17 | * [Test Commands](3d_test_commands.md) 18 | * [3D Rendering Engine](3d_rendering_engine.md) 19 | 20 | * [DS Sound](ds_sound.md) 21 | * [DS Peripherals](ds_peripherals.md) 22 | * [DS WiFi (Mitsumi)](ds_wifi.md) 23 | * [DS WiFi I/O Map](ds_wifi_io_map.md) 24 | * [DS WiFi General Registers](ds_wifi_general_regs.md) 25 | * [DSi Introduction](dsi_introduction.md) 26 | * [DSi I/O Map](dsi_io_map.md) 27 | * [DSi Control Registers](dsi_control_regs.md) 28 | * [DSi Shared WRAM](dsi_shared_wram.md) 29 | * [DSi New DMA (NDMA)](dsi_ndma.md) 30 | * [DSi Sound](dsi_sound.md) 31 | * [DSi Touch Screen](dsi_touch_screen.md) 32 | * [DSi I2C Bus](dsi_i2c_bus.md) 33 | * [DSi Cameras](dsi_cameras.md) 34 | * [DSi SD/MMC](dsi_sd_mmc.md) 35 | * [DSi GPIO](dsi_gpio.md) 36 | * [DSi AES Engine](dsi_aes.md) 37 | * [DSi WiFi (Atheros)](dsi_wifi.md) 38 | * [DSi DSP (XpertTeak)](dsi_dsp_xpertteak.md) 39 | * [Other](other.md) 40 | * [DS Cartridges](ds_cartridges.md) 41 | * [ROM Header](rom_header.md) 42 | * [CPU Reference](cpu_reference.md) 43 | * [BIOS Functions](bios.md) 44 | -------------------------------------------------------------------------------- /content/bios.md: -------------------------------------------------------------------------------- 1 | # DS BIOS Functions 2 | 3 | DS and DSi BIOS functions. Handling of interrupts and exceptions. 4 | -------------------------------------------------------------------------------- /content/cpu_reference.md: -------------------------------------------------------------------------------- 1 | # DS CPU References 2 | 3 | General description, links to ARM documentation (maybe we can add the PDF files to the repository). 4 | -------------------------------------------------------------------------------- /content/ds_2d_graphics.md: -------------------------------------------------------------------------------- 1 | # DS 2D Graphics 2 | 3 | Background, sprites, windows. 4 | 5 | -------------------------------------------------------------------------------- /content/ds_3d_graphics.md: -------------------------------------------------------------------------------- 1 | # DS 3D Graphics 2 | 3 | This is an introduction to the 3D graphics of the Nintendo DS. 4 | -------------------------------------------------------------------------------- /content/ds_cartridges.md: -------------------------------------------------------------------------------- 1 | # DS Cartridges 2 | 3 | How to read DS cartridges. Maybe something about flashcards. 4 | -------------------------------------------------------------------------------- /content/ds_introduction.md: -------------------------------------------------------------------------------- 1 | # Nintendo DS Introduction 2 | 3 | General introduction to the technical capabilities of the DS. 4 | -------------------------------------------------------------------------------- /content/ds_io_map.md: -------------------------------------------------------------------------------- 1 | # DS I/O Map 2 | 3 | I/O map of the NDS (excluding WiFi and 3D, see their chapters for more information). 4 | -------------------------------------------------------------------------------- /content/ds_mem_control.md: -------------------------------------------------------------------------------- 1 | # DS Memory Control 2 | 3 | Registers to map VRAM, WRAM, to setup waitstates for external cartridges, etc. 4 | -------------------------------------------------------------------------------- /content/ds_peripherals.md: -------------------------------------------------------------------------------- 1 | # DS Peripherals 2 | 3 | Math co-processors, IPC (FIFO), Timers, RTC, SPI, touch screen, power management. 4 | -------------------------------------------------------------------------------- /content/ds_sound.md: -------------------------------------------------------------------------------- 1 | # DS Sound 2 | 3 | Hardware sound channels, microphone, audio capture. 4 | -------------------------------------------------------------------------------- /content/ds_video.md: -------------------------------------------------------------------------------- 1 | # DS Video 2 | 3 | How 2D works from a high-level perspective, how 3D is displayed as part of a 2D layer. How screen capture works. 4 | 5 | 6 | -------------------------------------------------------------------------------- /content/ds_wifi.md: -------------------------------------------------------------------------------- 1 | # DS WiFi communications 2 | 3 | The DS supports IEEE 802.11b wireless communications. 4 | -------------------------------------------------------------------------------- /content/ds_wifi_general_regs.md: -------------------------------------------------------------------------------- 1 | # DS WiFi General Registers 2 | 3 | 4 | ## W\_ID: Chip ID (0x4808000, R) 5 | 6 | | Bit(s) | Description | 7 | |--------|---------------------------------------------------------| 8 | | 0-15 | Chip ID 9 | 10 | This register returns 0x1440 in NDS, 0xC340 in NDS lite. 11 | 12 | 13 | ## W\_MODE\_RST: Reset (0x4808004, R/W) 14 | 15 | | Bit(s) | Description | 16 | |--------|---------------------------------------------------------| 17 | | 0-15 | ??? 18 | 19 | 20 | ## W\_MODE\_WEP: WEP mode (0x4808006, R/W) 21 | 22 | | Bit(s) | Description | 23 | |--------|---------------------------------------------------------| 24 | | 0-2 | Software mode? 25 | | 3-5 | WEP key size 26 | 27 | 28 | ## W\_TXSTATCNT: Beacons status register (0x4808008, R/W) 29 | 30 | | Bit(s) | Description | 31 | |--------|---------------------------------------------------------| 32 | | 0-11 | ??? 33 | | 12 | 34 | | 13 | 35 | | 14 | 36 | | 15 | 37 | 38 | 39 | ## W\_X\_00A: Unknown (0x480800A, R/W) 40 | 41 | | Bit(s) | Description | 42 | |--------|---------------------------------------------------------| 43 | | 0-15 | ??? 44 | 45 | 46 | ## W\_IF: Interrupt request flags (0x4808010, R/W) 47 | 48 | | Bit(s) | Description | 49 | |--------|---------------------------------------------------------| 50 | | 0-15 | See [W\_IE](ds_wifi_general_regs.md#W_IE)) 51 | 52 | 53 | ## W\_IE: Interrupt enable (0x4808012, R/W) 54 | 55 | | Bit(s) | Description | 56 | |--------|---------------------------------------------------------| 57 | | 0 | 58 | | 1 | 59 | | 2 | 60 | | 3 | 61 | | 4 | 62 | | 5 | 63 | | 6 | 64 | | 7 | 65 | | 8 | 66 | | 9 | 67 | | 10 | Unused 68 | | 11 | 69 | | 12 | 70 | | 13 | 71 | | 14 | 72 | | 15 | 73 | 74 | 75 | ## W\_MACADDR\_0: Hardware MAC address (0x4808018, R/W) 76 | 77 | | Bit(s) | Description | 78 | |--------|---------------------------------------------------------| 79 | | 0-15 | 80 | 81 | 82 | ## W\_MACADDR\_1: Hardware MAC address (0x480801A, R/W) 83 | 84 | | Bit(s) | Description | 85 | |--------|---------------------------------------------------------| 86 | | 0-15 | 87 | 88 | 89 | ## W\_MACADDR\_2: Hardware MAC address (0x480801C, R/W) 90 | 91 | | Bit(s) | Description | 92 | |--------|---------------------------------------------------------| 93 | | 0-15 | 94 | 95 | 96 | ## W\_BSSID\_0: BSSID (0x4808020, R/W) 97 | 98 | | Bit(s) | Description | 99 | |--------|---------------------------------------------------------| 100 | | 0-15 | 101 | 102 | 103 | ## W\_BSSID\_1: BSSID (0x4808022, R/W) 104 | 105 | | Bit(s) | Description | 106 | |--------|---------------------------------------------------------| 107 | | 0-15 | 108 | 109 | 110 | ## W\_BSSID\_2: BSSID (0x4808024, R/W) 111 | 112 | | Bit(s) | Description | 113 | |--------|---------------------------------------------------------| 114 | | 0-15 | 115 | 116 | 117 | ## W\_AID\_LOW: Unknown (0x4808028, R/W) 118 | 119 | | Bit(s) | Description | 120 | |--------|---------------------------------------------------------| 121 | | 0-15 | 122 | 123 | 124 | ## W\_AID\_FULL: Association ID (0x480802A, R/W) 125 | 126 | | Bit(s) | Description | 127 | |--------|---------------------------------------------------------| 128 | | 0-15 | 129 | 130 | 131 | ## W\_TX\_RETRYLIMIT: TX retry limit (0x480802C, R/W) 132 | 133 | | Bit(s) | Description | 134 | |--------|---------------------------------------------------------| 135 | | 0-15 | 136 | 137 | 138 | ## W\_INTERNAL\_02E: Unknown (0x480802E, R/W) 139 | 140 | | Bit(s) | Description | 141 | |--------|---------------------------------------------------------| 142 | | 0-15 | Unknown 143 | 144 | 145 | ## W\_RXCNT: RX control (0x4808030, R/W) 146 | 147 | | Bit(s) | Description | 148 | |--------|---------------------------------------------------------| 149 | | 0-15 | 150 | 151 | 152 | ## W\_WEP\_CNT: WEP encryption enable (0x4808032, R/W) 153 | 154 | | Bit(s) | Description | 155 | |--------|---------------------------------------------------------| 156 | | 0-15 | 157 | 158 | 159 | ## W\_INTERNAL\_034: Unknown (0x4808034, R?) 160 | 161 | | Bit(s) | Description | 162 | |--------|---------------------------------------------------------| 163 | | 0-15 | Unknown 164 | -------------------------------------------------------------------------------- /content/ds_wifi_io_map.md: -------------------------------------------------------------------------------- 1 | # DS WiFi I/O Map 2 | 3 | The following registers are only accessible from the ARM7. They are located at 4 | addresses 0x4808000 to 0x4808FFF. Addresses not included in the following list 5 | aren't used. Also, many of the registers that are used aren't understood. 6 | 7 | Important note: 8-bit writes don't work on WiFi registers and RAM, they are 8 | ignored. 9 | 10 | 11 | ## General registers 12 | 13 | | Address | Access | Name | Description | 14 | |---------|--------|-------------------|---------------------------------------| 15 | | 4808000 | R | `W_ID` | Chip ID 16 | | 4808004 | R/W | `W_MODE_RST` | Reset 17 | | 4808006 | R/W | `W_MODE_WEP` | WEP and software modes 18 | | 4808008 | R/W | `W_TXSTATCNT` | Beacon status request 19 | | 480800A | R/W | `W_X_00A` | 20 | | 4808010 | R/W | `W_IF` | WiFi interrupt request flags 21 | | 4808012 | R/W | `W_IE` | WiFi interrupt enable 22 | | 4808018 | R/W | `W_MACADDR_0` | Hardware MAC address 23 | | 480801A | R/W | `W_MACADDR_1` | Hardware MAC address 24 | | 480801C | R/W | `W_MACADDR_2` | Hardware MAC address 25 | | 4808020 | R/W | `W_BSSID_0` | BSSID 26 | | 4808022 | R/W | `W_BSSID_1` | BSSID 27 | | 4808024 | R/W | `W_BSSID_2` | BSSID 28 | | 4808028 | R/W | `W_AID_LOW` | 29 | | 480802A | R/W | `W_AID_FULL` | Association ID 30 | | 480802C | R/W | `W_TX_RETRYLIMIT` | TX retry limit 31 | | 480802E | R/W | `W_INTERNAL_02E` | 32 | | 4808030 | R/W | `W_RXCNT` | RX control 33 | | 4808032 | R/W | `W_WEP_CNT` | WEP encryption enable 34 | | 4808034 | R? | `W_INTERNAL_034` | 35 | 36 | 37 | ## Power registers 38 | 39 | | Address | Access | Name | Description | 40 | |---------|--------|----------------|------------------------------------------| 41 | | 4808036 | R/W | `W_POWER_US` | 42 | | 4808038 | R/W | `W_POWER_TX` | 43 | | 480803C | R/W | `W_POWERSTATE` | 44 | | 4808040 | R/W | `W_POWERFORCE` | 45 | | 4808044 | R | `W_RANDOM` | 46 | | 4808048 | R/W | `W_POWER_048` | 47 | 48 | 49 | ## Receive control 50 | 51 | | Address | Access | Name | Description | 52 | |---------|--------|-------------------|---------------------------------------| 53 | | 4808050 | R/W | `W_RXBUF_BEGIN` | 54 | | 4808052 | R/W | `W_RXBUF_END` | 55 | | 4808054 | R | `W_RXBUF_WRCSR` | 56 | | 4808056 | R/W | `W_RXBUF_WR_ADDR` | 57 | | 4808058 | R/W | `W_RXBUF_RD_ADDR` | 58 | | 480805A | R/W | `W_RXBUF_READCSR` | 59 | | 480805C | R/W | `W_RXBUF_COUNT` | 60 | | 4808060 | R | `W_RXBUF_RD_DATA` | 61 | | 4808062 | R/W | `W_RXBUF_GAP` | 62 | | 4808064 | R/W | `W_RXBUF_GAPDISP` | 63 | 64 | 65 | ## Transmit control 66 | 67 | | Address | Access | Name | Description | 68 | |---------|--------|-------------------|---------------------------------------| 69 | | 4808068 | R/W | `W_TXBUF_WR_ADDR` | 70 | | 480806C | R/W | `W_TXBUF_COUNT` | 71 | | 4808070 | W | `W_TXBUF_WR_DATA` | 72 | | 4808074 | R/W | `W_TXBUF_GAP` | 73 | | 4808076 | R/W | `W_TXBUF_GAPDISP` | 74 | | 4808078 | W | `W_INTERNAL_078` | 75 | | 4808080 | R/W | `W_TXBUF_BEACON` | Beacon transmit location 76 | | 4808084 | R/W | `W_TXBUF_TIM` | Beacon TIM index in frame body 77 | | 4808088 | R/W | `W_LISTENCOUNT` | Listen count 78 | | 480808C | R/W | `W_BEACONINT` | Beacon interval 79 | | 480808E | R/W | `W_LISTENINT` | Listen interval 80 | | 4808090 | R/W | `W_TXBUF_CMD` | Multiplay command 81 | | 4808094 | R/W | `W_TXBUF_REPLY1` | Multiplay next reply 82 | | 4808098 | R | `W_TXBUF_REPLY2` | Multiplay current reply 83 | | 480809C | R/W | `W_INTERNAL_09C` | 84 | | 48080A0 | R/W | `W_TXBUF_LOC1` | 85 | | 48080A4 | R/W | `W_TXBUF_LOC2` | 86 | | 48080A8 | R/W | `W_TXBUF_LOC3` | 87 | | 48080AC | W | `W_TXREQ_RESET` | 88 | | 48080AE | W | `W_TXREQ_SET` | 89 | | 48080B0 | R | `W_TXREQ_READ` | 90 | | 48080B4 | W | `W_TXBUF_RESET` | 91 | | 48080B6 | R | `W_TXBUSY` | 92 | | 48080B8 | R | `W_TXSTAT` | 93 | | 48080BA | ? | `W_INTERNAL_0BA` | 94 | | 48080BC | R/W | `W_PREAMBLE` | 95 | | 48080C0 | R/W | `W_CMD_TOTALTIME` | 96 | | 48080C4 | R/W | `W_CMD_REPLYTIME` | 97 | | 48080C8 | ? | `W_INTERNAL_0C8` | 98 | | 48080D0 | R/W | `W_RXFILTER` | 99 | | 48080D4 | R/W | `W_CONFIG_0D4` | 100 | | 48080D8 | R/W | `W_CONFIG_0D8` | 101 | | 48080DA | R/W | `W_RX_LEN_CROP` | 102 | | 48080E0 | R/W | `W_RXFILTER2` | 103 | 104 | 105 | ## WiFi timers 106 | 107 | | Address | Access | Name | Description | 108 | |---------|--------|-------------------|---------------------------------------| 109 | | 48080E8 | R/W | `W_US_COUNTCNT` | Microsecond counter enable 110 | | 48080EA | R/W | `W_US_COMPARECNT` | Microsecond compare enable 111 | | 48080EC | R/W | `W_CONFIG_0EC` | 112 | | 48080EE | R/W | `W_CMD_COUNTCNT` | 113 | | 48080F0 | R/W | `W_US_COMPARE0` | Microsecond compare value 114 | | 48080F2 | R/W | `W_US_COMPARE1` | Microsecond compare value 115 | | 48080F4 | R/W | `W_US_COMPARE2` | Microsecond compare value 116 | | 48080F6 | R/W | `W_US_COMPARE3` | Microsecond compare value 117 | | 48080F8 | R/W | `W_US_COUNT0` | Microsecond counter 118 | | 48080FA | R/W | `W_US_COUNT1` | Microsecond counter 119 | | 48080FC | R/W | `W_US_COUNT2` | Microsecond counter 120 | | 48080FE | R/W | `W_US_COUNT3` | Microsecond counter 121 | | 4808100 | ? | `W_INTERNAL_100` | 122 | | 4808102 | ? | `W_INTERNAL_102` | 123 | | 4808104 | ? | `W_INTERNAL_104` | 124 | | 4808106 | ? | `W_INTERNAL_106` | 125 | | 480810C | R/W | `W_CONTENTFREE` | 126 | | 4808110 | R/W | `W_PRE_BEACON` | 127 | | 4808118 | R/W | `W_CMD_COUNT` | 128 | | 480811C | R/W | `W_BEACON_COUNT` | 129 | 130 | 131 | ## Configuration ports 132 | 133 | | Address | Access | Name | Description | 134 | |---------|--------|------------------|----------------------------------------| 135 | | 4808120 | R/W | `W_CONFIG_120` | 136 | | 4808122 | R/W | `W_CONFIG_122` | 137 | | 4808124 | R/W | `W_CONFIG_124` | 138 | | 4808126 | ? | `W_INTERNAL_126` | 139 | | 4808128 | R/W | `W_CONFIG_128` | 140 | | 480812A | ? | `W_INTERNAL_12A` | 141 | | 4808130 | R/W | `W_CONFIG_130` | 142 | | 4808132 | R/W | `W_CONFIG_132` | 143 | | 4808134 | R/W | `W_POST_BEACON` | 144 | | 4808140 | R/W | `W_CONFIG_140` | 145 | | 4808142 | R/W | `W_CONFIG_142` | 146 | | 4808144 | R/W | `W_CONFIG_144` | 147 | | 4808146 | R/W | `W_CONFIG_146` | 148 | | 4808148 | R/W | `W_CONFIG_148` | 149 | | 480814A | R/W | `W_CONFIG_14A` | 150 | | 480814C | R/W | `W_CONFIG_14C` | 151 | | 4808150 | R/W | `W_CONFIG_150` | 152 | | 4808154 | R/W | `W_CONFIG_154` | 153 | 154 | 155 | ## Baseband chip 156 | 157 | | Address | Access | Name | Description | 158 | |---------|--------|-----------------|-----------------------------------------| 159 | | 4808158 | W | `W_BB_CNT` | BB access control 160 | | 480815A | W | `W_BB_WRITE` | Byte to write to BB 161 | | 480815C | R | `W_BB_READ` | Byte read from BB 162 | | 480815E | R | `W_BB_BUSY` | BB access busy Flag 163 | | 4808160 | R/W | `W_BB_MODE` | BB access mode 164 | | 4808168 | R/W | `W_BB_POWER` | BB access powerdown 165 | 166 | 167 | ## Internal registers 168 | 169 | | Address | Access | Name | Description | 170 | |---------|--------|------------------|----------------------------------------| 171 | | 480816A | ? | `W_INTERNAL_16A` | 172 | | 4808170 | ? | `W_INTERNAL_170` | 173 | | 4808172 | ? | `W_INTERNAL_172` | 174 | | 4808174 | ? | `W_INTERNAL_174` | 175 | | 4808176 | ? | `W_INTERNAL_176` | 176 | | 4808178 | W | `W_INTERNAL_178` | 177 | 178 | 179 | ## RF chip 180 | 181 | | Address | Access | Name | Description | 182 | |---------|--------|------------------|----------------------------------------| 183 | | 480817C | R/W | `W_RF_DATA2` | 184 | | 480817E | R/W | `W_RF_DATA1` | 185 | | 4808180 | R | `W_RF_BUSY` | 186 | | 4808184 | R/W | `W_RF_CNT` | 187 | | 4808190 | R/W | `W_INTERNAL_190` | 188 | | 4808194 | R/W | `W_TX_HDR_CNT` | 189 | | 4808198 | R/W | `W_INTERNAL_198` | 190 | | 480819C | R | `W_RF_PINS` | 191 | | 48081A0 | R/W | `W_X_1A0` | 192 | | 48081A2 | R/W | `W_X_1A2` | 193 | | 48081A4 | R/W | `W_X_1A4` | 194 | 195 | 196 | ## WiFi statistics 197 | 198 | | Address | Access | Name | Description | 199 | |---------|--------|-------------------|---------------------------------------| 200 | | 48081A8 | R | `W_RXSTAT_INC_IF` | Stats increment flags 201 | | 48081AA | R/W | `W_RXSTAT_INC_IE` | Stats increment IRQ enable 202 | | 48081AC | R | `W_RXSTAT_OVF_IF` | Stats half-overflow flags 203 | | 48081AE | R/W | `W_RXSTAT_OVF_IE` | Stats half-overflow IRQ enable 204 | | 48081B0 | R/W | `W_RXSTAT` | 205 | | 48081B2 | R/W | `W_RXSTAT` | 206 | | 48081B4 | R/W | `W_RXSTAT` | 207 | | 48081B6 | R/W | `W_RXSTAT` | 208 | | 48081B8 | R/W | `W_RXSTAT` | 209 | | 48081BA | R/W | `W_RXSTAT` | 210 | | 48081BC | R/W | `W_RXSTAT` | 211 | | 48081BE | R/W | `W_RXSTAT` | 212 | | 48081C0 | R/W | `W_TX_ERR_COUNT` | TX error count 213 | | 48081C4 | R | `W_RX_COUNT` | 214 | | 48081D0 | R/W | `W_CMD_STAT` | 215 | | 48081D2 | R/W | `W_CMD_STAT` | 216 | | 48081D4 | R/W | `W_CMD_STAT` | 217 | | 48081D6 | R/W | `W_CMD_STAT` | 218 | | 48081D8 | R/W | `W_CMD_STAT` | 219 | | 48081DA | R/W | `W_CMD_STAT` | 220 | | 48081DC | R/W | `W_CMD_STAT` | 221 | | 48081DE | R/W | `W_CMD_STAT` | 222 | 223 | 224 | ## Internal diagnostics 225 | 226 | | Address | Access | Name | Description | 227 | |---------|--------|------------------|----------------------------------------| 228 | | 48081F0 | R/W | `W_INTERNAL_1F0` | 229 | | 4808204 | ? | `W_INTERNAL_204` | 230 | | 4808208 | ? | `W_INTERNAL_208` | 231 | | 480820C | W | `W_INTERNAL_20C` | 232 | | 4808210 | R | `W_TX_SEQNO` | 233 | | 4808214 | R | `W_RF_STATUS` | 234 | | 480821C | W | `W_IF_SET` | Set bits in `W_IF` to force interrupts. 235 | | 4808220 | R/W | `W_RAM_DISABLE` | WiFi RAM control 236 | | 4808224 | R/W | `W_INTERNAL_224` | 237 | | 4808228 | W | `W_X_228` | 238 | | 4808230 | R/W | `W_INTERNAL_230` | 239 | | 4808234 | R/W | `W_INTERNAL_234` | 240 | | 4808238 | R/W | `W_INTERNAL_238` | 241 | | 480823C | ? | `W_INTERNAL_23C` | 242 | | 4808244 | R/W | `W_X_244` | 243 | | 4808248 | R/W | `W_INTERNAL_248` | 244 | | 480824C | R | `W_INTERNAL_24C` | 245 | | 480824E | R | `W_INTERNAL_24E` | 246 | | 4808250 | R | `W_INTERNAL_250` | 247 | | 4808254 | ? | `W_CONFIG_254` | 248 | | 4808258 | ? | `W_INTERNAL_258` | 249 | | 480825C | ? | `W_INTERNAL_25C` | 250 | | 4808260 | ? | `W_INTERNAL_260` | 251 | | 4808264 | R | `W_INTERNAL_264` | 252 | | 4808268 | R | `W_RXTX_ADDR` | 253 | | 4808270 | R | `W_INTERNAL_270` | 254 | | 4808274 | ? | `W_INTERNAL_274` | 255 | | 4808278 | R/W | `W_INTERNAL_278` | 256 | | 480827C | ? | `W_INTERNAL_27C` | 257 | | 4808290 | (R/W) | `W_X_290` | 258 | | 4808298 | W | `W_INTERNAL_298` | 259 | | 48082A0 | R/W | `W_INTERNAL_2A0` | 260 | | 48082A2 | R | `W_INTERNAL_2A2` | 261 | | 48082A4 | R | `W_INTERNAL_2A4` | 262 | | 48082A8 | W | `W_INTERNAL_2A8` | 263 | | 48082AC | ? | `W_INTERNAL_2AC` | 264 | | 48082B0 | W | `W_INTERNAL_2B0` | 265 | | 48082B4 | R/W | `W_INTERNAL_2B4` | 266 | | 48082B8 | ? | `W_INTERNAL_2B8` | 267 | | 48082C0 | R/W | `W_INTERNAL_2C0` | 268 | | 48082C4 | R | `W_INTERNAL_2C4` | 269 | | 48082C8 | R | `W_INTERNAL_2C8` | 270 | | 48082CC | R | `W_INTERNAL_2CC` | 271 | | 48082D0 | ? | `W_INTERNAL_2D0` | 272 | | 48082F0 | R/W | `W_INTERNAL_2F0` | 273 | | 48082F2 | R/W | `W_INTERNAL_2F2` | 274 | | 48082F4 | R/W | `W_INTERNAL_2F4` | 275 | | 48082F6 | R/W | `W_INTERNAL_2F6` | 276 | -------------------------------------------------------------------------------- /content/dsi_aes.md: -------------------------------------------------------------------------------- 1 | # DSi AES Engine 2 | 3 | -------------------------------------------------------------------------------- /content/dsi_cameras.md: -------------------------------------------------------------------------------- 1 | # DSi Cameras 2 | 3 | Front and back cameras of the DSi. 4 | -------------------------------------------------------------------------------- /content/dsi_control_regs.md: -------------------------------------------------------------------------------- 1 | # DSi Control Registers 2 | 3 | SCFG, MBK registers. 4 | -------------------------------------------------------------------------------- /content/dsi_dsp_xpertteak.md: -------------------------------------------------------------------------------- 1 | # DSi DSP XpertTeak 2 | 3 | -------------------------------------------------------------------------------- /content/dsi_gpio.md: -------------------------------------------------------------------------------- 1 | # DSi GPIO 2 | 3 | -------------------------------------------------------------------------------- /content/dsi_i2c_bus.md: -------------------------------------------------------------------------------- 1 | # DSi I2C Bus 2 | 3 | I2C Bus usage. 4 | -------------------------------------------------------------------------------- /content/dsi_introduction.md: -------------------------------------------------------------------------------- 1 | # Nintendo DSi Introduction 2 | 3 | General introduction to the technical capabilities of the DSi, differences with the DS. 4 | -------------------------------------------------------------------------------- /content/dsi_io_map.md: -------------------------------------------------------------------------------- 1 | # DSi I/O Map 2 | 3 | New registers added in DSi (excluding WiFi and DSP, see their chapters for more information). 4 | -------------------------------------------------------------------------------- /content/dsi_ndma.md: -------------------------------------------------------------------------------- 1 | # DSi New DMA (NDMA) 2 | 3 | New DMA channels. 4 | -------------------------------------------------------------------------------- /content/dsi_sd_mmc.md: -------------------------------------------------------------------------------- 1 | # DSi SD and MMC 2 | 3 | How to access the SD and the NAND. 4 | -------------------------------------------------------------------------------- /content/dsi_shared_wram.md: -------------------------------------------------------------------------------- 1 | # DSi Shared WRAM 2 | 3 | New shared WRAM. 4 | -------------------------------------------------------------------------------- /content/dsi_sound.md: -------------------------------------------------------------------------------- 1 | # DSi Sound 2 | 3 | New microphone, getting sound into and out of the DSP. 4 | -------------------------------------------------------------------------------- /content/dsi_touch_screen.md: -------------------------------------------------------------------------------- 1 | # DSi Touch Screen 2 | 3 | Touch screen information. 4 | -------------------------------------------------------------------------------- /content/dsi_wifi.md: -------------------------------------------------------------------------------- 1 | # DSi WiFi communications 2 | 3 | The DSi supports IEEE 802.11b wireless communications. 4 | -------------------------------------------------------------------------------- /content/fonts/Font_LICENSE: -------------------------------------------------------------------------------- 1 | Source Sans: Copyright 2010-2022 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. 2 | 3 | Fira Code: Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode) 4 | 5 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 6 | This license is copied below, and is also available with a FAQ at: 7 | http://scripts.sil.org/OFL 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /content/fonts/SourceSans3-It.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/SourceSans3-It.otf.woff -------------------------------------------------------------------------------- /content/fonts/SourceSans3-Regular.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/SourceSans3-Regular.otf.woff -------------------------------------------------------------------------------- /content/fonts/SourceSans3-Semibold.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/SourceSans3-Semibold.otf.woff -------------------------------------------------------------------------------- /content/fonts/SourceSans3-SemiboldIt.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/SourceSans3-SemiboldIt.otf.woff -------------------------------------------------------------------------------- /content/fonts/fira_code.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Fira Code'; 3 | src: url('woff2/FiraCode-Light.woff2') format('woff2'), 4 | url("woff/FiraCode-Light.woff") format("woff"); 5 | font-weight: 300; 6 | font-style: normal; 7 | } 8 | 9 | @font-face { 10 | font-family: 'Fira Code'; 11 | src: url('woff2/FiraCode-Regular.woff2') format('woff2'), 12 | url("woff/FiraCode-Regular.woff") format("woff"); 13 | font-weight: 400; 14 | font-style: normal; 15 | } 16 | 17 | @font-face { 18 | font-family: 'Fira Code'; 19 | src: url('woff2/FiraCode-Medium.woff2') format('woff2'), 20 | url("woff/FiraCode-Medium.woff") format("woff"); 21 | font-weight: 500; 22 | font-style: normal; 23 | } 24 | 25 | @font-face { 26 | font-family: 'Fira Code'; 27 | src: url('woff2/FiraCode-SemiBold.woff2') format('woff2'), 28 | url("woff/FiraCode-SemiBold.woff") format("woff"); 29 | font-weight: 600; 30 | font-style: normal; 31 | } 32 | 33 | @font-face { 34 | font-family: 'Fira Code'; 35 | src: url('woff2/FiraCode-Bold.woff2') format('woff2'), 36 | url("woff/FiraCode-Bold.woff") format("woff"); 37 | font-weight: 700; 38 | font-style: normal; 39 | } 40 | 41 | @font-face { 42 | font-family: 'Fira Code VF'; 43 | src: url('woff2/FiraCode-VF.woff2') format('woff2-variations'), 44 | url('woff/FiraCode-VF.woff') format('woff-variations'); 45 | /* font-weight requires a range: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide#Using_a_variable_font_font-face_changes */ 46 | font-weight: 300 700; 47 | font-style: normal; 48 | } -------------------------------------------------------------------------------- /content/fonts/woff/FiraCode-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff/FiraCode-Bold.woff -------------------------------------------------------------------------------- /content/fonts/woff/FiraCode-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff/FiraCode-Light.woff -------------------------------------------------------------------------------- /content/fonts/woff/FiraCode-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff/FiraCode-Medium.woff -------------------------------------------------------------------------------- /content/fonts/woff/FiraCode-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff/FiraCode-Regular.woff -------------------------------------------------------------------------------- /content/fonts/woff/FiraCode-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff/FiraCode-SemiBold.woff -------------------------------------------------------------------------------- /content/fonts/woff/FiraCode-VF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff/FiraCode-VF.woff -------------------------------------------------------------------------------- /content/fonts/woff2/FiraCode-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff2/FiraCode-Bold.woff2 -------------------------------------------------------------------------------- /content/fonts/woff2/FiraCode-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff2/FiraCode-Light.woff2 -------------------------------------------------------------------------------- /content/fonts/woff2/FiraCode-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff2/FiraCode-Medium.woff2 -------------------------------------------------------------------------------- /content/fonts/woff2/FiraCode-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff2/FiraCode-Regular.woff2 -------------------------------------------------------------------------------- /content/fonts/woff2/FiraCode-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff2/FiraCode-SemiBold.woff2 -------------------------------------------------------------------------------- /content/fonts/woff2/FiraCode-VF.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/ndsdoc/0f156ee1c74eeb52bdb0ff4a39c4c5236588eed4/content/fonts/woff2/FiraCode-VF.woff2 -------------------------------------------------------------------------------- /content/foreword.md: -------------------------------------------------------------------------------- 1 | # Foreword 2 | 3 | Description 4 | 5 | :::warning Work In Progress notice 6 | 7 | Some text 8 | 9 | ::: 10 | 11 | ## Contributing 12 | 13 | This book is open source, released under the [CC0](https://raw.githubusercontent.com/gbadev-org/ndsdoc/master/LICENSE-CC0.txt) license. Everyone is welcome to help, provide feedback and propose additions or improvements. The git repository is hosted at [github.com/gbadev-org/ndsdoc](https://github.com/gbadev-org/ndsdoc), where you can learn more about how you can help, find detailed contribution guidelines and procedures, file Issues and send Pull Requests. 14 | 15 | 16 | ## Using this document 17 | 18 | In the top navigation bar, you will find a series of icons. 19 | 20 | By clicking on the icon you will toggle an interactive table of contents to navigate the document. You can also use and keys on your keyboard to go to the following and previous page. 21 | 22 | The lets you choose among 6 different themes and color schemes to please your reading experience, including the classic Tonc theme. 23 | 24 | You can search anywhere by pressing s on your keyboard or clicking the icon. 25 | 26 | The icon allows you to suggest an edit on the current page by directly opening the source file in the git repository. 27 | 28 |
-------------------------------------------------------------------------------- /content/other.md: -------------------------------------------------------------------------------- 1 | # Other 2 | 3 | Section for other information that is shared, or that doesn't belong to either console model. 4 | -------------------------------------------------------------------------------- /content/rom_header.md: -------------------------------------------------------------------------------- 1 | # DS ROM Header 2 | 3 | DS ROM header, differences with DSi ROM header. 4 | -------------------------------------------------------------------------------- /mdbook-external-links/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /mdbook-external-links/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mdbook-external-links" 3 | description = "Open external links inside your mdBooks in a different tab" 4 | keywords = [ 5 | "mdbook", 6 | "external", 7 | "links", 8 | "mdbook-plugin", 9 | "mdbook-preprocessor", 10 | ] 11 | version = "0.1.1" 12 | edition = "2021" 13 | repository = "https://github.com/jonahgoldwastaken/mdbook-external-links" 14 | license = "MPL-2.0" 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [dependencies] 19 | anyhow = "1.0.68" 20 | clap = "4.1.1" 21 | mdbook = "0.4.25" 22 | pulldown-cmark = "0.9.2" 23 | pulldown-cmark-to-cmark = "10.0.4" 24 | semver = "1.0.16" 25 | serde_json = "1.0.91" 26 | -------------------------------------------------------------------------------- /mdbook-external-links/README.md: -------------------------------------------------------------------------------- 1 | # mdbook-external-links 2 | 3 | Open external links inside your mdBooks in a different tab. 4 | 5 | ## Installation & usage 6 | 7 | Install through `cargo` 8 | 9 | ```bash 10 | cargo install mdbook-external-links 11 | ``` 12 | 13 | Include it in your `book.toml` 14 | 15 | ```toml 16 | [preprocessor.external-links] 17 | ``` 18 | 19 | That's it! 🚀 20 | 21 | ## What counts as an external link? 22 | 23 | - [Inline](https://spec.commonmark.org/0.30/#inline-link), [reference](https://spec.commonmark.org/0.30/#full-reference-link), [shortcut](https://spec.commonmark.org/0.30/#shortcut-reference-link), and [collapsed](https://spec.commonmark.org/0.30/#collapsed-reference-link) links that start with http(s) (other protocols are missing at the moment). 24 | - Any [autolink](https://spec.commonmark.org/0.30/#autolinks) 25 | -------------------------------------------------------------------------------- /mdbook-external-links/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{Arg, ArgMatches, Command}; 2 | use exl_lib::Exl; 3 | use mdbook::{ 4 | book::{Book, Chapter}, 5 | errors::Result, 6 | preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext}, 7 | }; 8 | use semver::{Version, VersionReq}; 9 | use std::io; 10 | use std::process; 11 | 12 | pub fn make_app() -> Command { 13 | Command::new("external-links-preprocessor") 14 | .about(r#"A mdbook preprocessor that adds 'target="_blank"' to anchor tags"#) 15 | .subcommand( 16 | Command::new("supports") 17 | .arg(Arg::new("renderer").required(true)) 18 | .about("Check whether a renderer is supported by this preprocessor"), 19 | ) 20 | } 21 | 22 | fn main() { 23 | let matches = make_app().get_matches(); 24 | 25 | let preprocessor = Exl::new(); 26 | if let Some(sub_args) = matches.subcommand_matches("supports") { 27 | handle_supports(&preprocessor, sub_args); 28 | } else if let Err(e) = handle_preprocessing(&preprocessor) { 29 | eprintln!("{}", e); 30 | process::exit(1); 31 | } 32 | } 33 | 34 | fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<()> { 35 | let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?; 36 | 37 | let book_version = Version::parse(&ctx.mdbook_version)?; 38 | let version_req = VersionReq::parse(mdbook::MDBOOK_VERSION)?; 39 | 40 | if !version_req.matches(&book_version) { 41 | eprintln!( 42 | "Warning: The {} plugin was built against version {} of mdbook, \ 43 | but we're being called from version {}", 44 | pre.name(), 45 | mdbook::MDBOOK_VERSION, 46 | ctx.mdbook_version 47 | ); 48 | } 49 | 50 | let processed_book = pre.run(&ctx, book)?; 51 | serde_json::to_writer(io::stdout(), &processed_book)?; 52 | 53 | Ok(()) 54 | } 55 | 56 | fn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! { 57 | let renderer = sub_args 58 | .get_one::("renderer") 59 | .expect("Required argument"); 60 | 61 | let supported = pre.supports_renderer(renderer); 62 | 63 | if supported { 64 | process::exit(0); 65 | } else { 66 | process::exit(1); 67 | } 68 | } 69 | 70 | mod exl_lib { 71 | use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag}; 72 | use pulldown_cmark_to_cmark::cmark; 73 | 74 | use super::*; 75 | 76 | pub struct Exl; 77 | 78 | impl Exl { 79 | pub fn new() -> Self { 80 | Self 81 | } 82 | 83 | fn replace_anchors(&self, chapter: &mut Chapter) -> Result { 84 | let mut buf = String::with_capacity(chapter.content.len()); 85 | 86 | let events = Parser::new_ext(&chapter.content, Options::all()).map(|e| { 87 | let ev = match &e { 88 | Event::Start(Tag::Link(..)) => "start", 89 | Event::End(Tag::Link(..)) => "end", 90 | _ => "other", 91 | }; 92 | if ev == "other" { 93 | return e; 94 | } 95 | let (lt, url, title) = match &e { 96 | Event::Start(Tag::Link(lt, url, title)) => (lt, url, title), 97 | Event::End(Tag::Link(lt, url, title)) => (lt, url, title), 98 | _ => unreachable!(), 99 | }; 100 | 101 | match lt { 102 | LinkType::Shortcut 103 | | LinkType::Inline 104 | | LinkType::Reference 105 | | LinkType::Collapsed => { 106 | if url.starts_with("http") { 107 | if ev == "end" { 108 | Event::Html(CowStr::from("")) 109 | } else { 110 | Event::Html(CowStr::from(format!( 111 | r#""# 112 | ))) 113 | } 114 | } else { 115 | e 116 | } 117 | } 118 | LinkType::Email => { 119 | if ev == "end" { 120 | Event::Html(CowStr::from("")) 121 | } else { 122 | Event::Html(CowStr::from(format!(r#""#))) 123 | } 124 | } 125 | LinkType::Autolink => { 126 | if ev == "end" { 127 | Event::Html(CowStr::from("")) 128 | } else { 129 | Event::Html(CowStr::from(format!( 130 | r#""# 131 | ))) 132 | } 133 | } 134 | LinkType::ReferenceUnknown => e, 135 | LinkType::CollapsedUnknown => e, 136 | LinkType::ShortcutUnknown => e, 137 | } 138 | }); 139 | 140 | cmark(events, &mut buf) 141 | .map(|_| buf) 142 | .map_err(|err| anyhow::anyhow!("Markdown serialization failed: {err}")) 143 | } 144 | } 145 | 146 | impl Preprocessor for Exl { 147 | fn name(&self) -> &str { 148 | "external-links-preprocessor" 149 | } 150 | 151 | fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result { 152 | book.for_each_mut(|bi| match bi { 153 | mdbook::BookItem::Chapter(ref mut c) => { 154 | c.content = self 155 | .replace_anchors(c) 156 | .expect("Error converting links for chapter"); 157 | } 158 | mdbook::BookItem::Separator | mdbook::BookItem::PartTitle(_) => {} 159 | }); 160 | Ok(book) 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /mdbook-toc/.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/starship/starship/blob/master/.github/workflows/deploy.yml 2 | 3 | name: Deploy 4 | on: 5 | push: 6 | tags: 7 | - "*" 8 | 9 | env: 10 | CRATE_NAME: mdbook-toc 11 | 12 | jobs: 13 | # Build sources for every OS 14 | github_build: 15 | name: Build release binaries 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | target: 20 | - x86_64-unknown-linux-gnu 21 | - x86_64-unknown-linux-musl 22 | - x86_64-apple-darwin 23 | - aarch64-apple-darwin 24 | - x86_64-pc-windows-msvc 25 | include: 26 | - target: x86_64-unknown-linux-gnu 27 | os: ubuntu-latest 28 | name: x86_64-unknown-linux-gnu.tar.gz 29 | - target: x86_64-unknown-linux-musl 30 | os: ubuntu-latest 31 | name: x86_64-unknown-linux-musl.tar.gz 32 | - target: x86_64-apple-darwin 33 | os: macOS-latest 34 | name: x86_64-apple-darwin.tar.gz 35 | - target: aarch64-apple-darwin 36 | os: macOS-latest 37 | name: aarch64-apple-darwin.tar.gz 38 | - target: x86_64-pc-windows-msvc 39 | os: windows-latest 40 | name: x86_64-pc-windows-msvc.zip 41 | runs-on: ${{ matrix.os }} 42 | steps: 43 | - name: Setup | Checkout 44 | uses: actions/checkout@v3 45 | 46 | - name: Setup | Rust 47 | uses: dtolnay/rust-toolchain@stable 48 | with: 49 | target: ${{ matrix.target }} 50 | 51 | - uses: Swatinem/rust-cache@v2 52 | 53 | - name: Setup | musl tools 54 | if: matrix.target == 'x86_64-unknown-linux-musl' 55 | run: sudo apt install -y musl-tools 56 | 57 | - name: Build | Build 58 | if: matrix.target != 'x86_64-unknown-linux-musl' 59 | run: cargo build --release --target ${{ matrix.target }} 60 | 61 | - name: Build | Build (musl) 62 | if: matrix.target == 'x86_64-unknown-linux-musl' 63 | run: cargo build --release --target ${{ matrix.target }} 64 | 65 | - name: Post Setup | Extract tag name 66 | shell: bash 67 | run: echo "##[set-output name=tag;]$(echo ${GITHUB_REF#refs/tags/})" 68 | id: extract_tag 69 | 70 | - name: Post Setup | Prepare artifacts [Windows] 71 | if: matrix.os == 'windows-latest' 72 | run: | 73 | mkdir target/stage 74 | cd target/${{ matrix.target }}/release 75 | strip ${{ env.CRATE_NAME }}.exe 76 | 7z a ../../stage/${{ env.CRATE_NAME }}-${{ steps.extract_tag.outputs.tag }}-${{ matrix.name }} ${{ env.CRATE_NAME }}.exe 77 | cd - 78 | 79 | - name: Post Setup | Prepare artifacts [-nix] 80 | if: matrix.os != 'windows-latest' 81 | run: | 82 | mkdir target/stage 83 | cd target/${{ matrix.target }}/release 84 | strip ${{ env.CRATE_NAME }} 85 | tar czvf ../../stage/${{ env.CRATE_NAME }}-${{ steps.extract_tag.outputs.tag }}-${{ matrix.name }} ${{ env.CRATE_NAME }} 86 | cd - 87 | 88 | - name: Post Setup | Upload artifacts 89 | uses: actions/upload-artifact@v3 90 | with: 91 | name: ${{ env.CRATE_NAME }}-${{ steps.extract_tag.outputs.tag }}-${{ matrix.name }} 92 | path: target/stage/* 93 | 94 | # Create GitHub release with Rust build targets and release notes 95 | github_release: 96 | name: Create GitHub Release 97 | needs: github_build 98 | runs-on: ubuntu-latest 99 | steps: 100 | - name: Setup | Checkout 101 | uses: actions/checkout@v3 102 | with: 103 | fetch-depth: 0 104 | 105 | - name: Setup | Artifacts 106 | uses: actions/download-artifact@v3 107 | 108 | - name: Setup | Release notes 109 | run: | 110 | git log -1 --pretty='%s' > RELEASE.md 111 | 112 | - name: Build | Publish 113 | uses: softprops/action-gh-release@v1 114 | with: 115 | files: ${{ env.CRATE_NAME }}-*/${{ env.CRATE_NAME }}-* 116 | body_path: RELEASE.md 117 | env: 118 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 119 | -------------------------------------------------------------------------------- /mdbook-toc/.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | RUSTFLAGS: -Dwarnings 7 | 8 | jobs: 9 | build_and_test: 10 | name: Build and test 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: dtolnay/rust-toolchain@stable 16 | 17 | - name: tests 18 | uses: actions-rs/cargo@v1 19 | with: 20 | command: test 21 | args: --all 22 | 23 | check_fmt_and_docs: 24 | name: Checking fmt and docs 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v3 28 | - uses: dtolnay/rust-toolchain@stable 29 | with: 30 | components: rustfmt 31 | 32 | - name: fmt 33 | run: cargo fmt --all -- --check 34 | 35 | - name: Docs 36 | run: cargo doc --no-deps 37 | -------------------------------------------------------------------------------- /mdbook-toc/.gitignore: -------------------------------------------------------------------------------- 1 | /book 2 | /target 3 | -------------------------------------------------------------------------------- /mdbook-toc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.14.1 (2023-08-05) 2 | 3 | * Upgrade mdbook and downgrade toml 4 | 5 | # v0.14.0 (2023-08-02) 6 | 7 | * Use custom header IDs if present 8 | * Upgrade dependencies 9 | 10 | # v0.13.0 (2023-07-18) 11 | 12 | * Upgrade dependencies 13 | 14 | # v0.12.0 (2023-05-15) 15 | 16 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.11.2...0.12.0) 17 | 18 | * Normalize to LF line endings so that the default marker works on CRLF documents 19 | 20 | # v0.11.2 (2023-02-14) 21 | 22 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.11.1...0.11.2) 23 | 24 | * Upgrade dependencies and avoid breakage (v0.11.1 was yanked) 25 | 26 | # v0.11.1 (2023-02-09) 27 | 28 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.11.0...0.11.1) 29 | 30 | * Upgrade dependencies 31 | 32 | # v0.11.0 (2022-12-15) 33 | 34 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.10.0...0.11.0) 35 | 36 | * Upgrade dependencies 37 | 38 | # v0.10.0 (2022-10-11) 39 | 40 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.9.0...0.10.0) 41 | 42 | * Bump to mdbook 0.4.21 43 | * Dependency updates 44 | 45 | # v0.9.0 (2022-05-26) 46 | 47 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.8.0...0.9.0) 48 | 49 | * Dependency updates 50 | 51 | # v0.8.0 (2022-01-26) 52 | 53 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.7.0...0.8.0) 54 | 55 | * Avoid roundtripping through pulldown-cmark 56 | 57 | # v0.7.0 (2021-07-06) 58 | 59 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.6.4...0.7.0) 60 | 61 | * Bump pulldown dependency to fix issue with table rendering 62 | 63 | # v0.6.4 (2021-06-11) 64 | 65 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.6.3...0.6.4) 66 | 67 | * Bump to mdbook v0.4.10 68 | 69 | # v0.6.3 (2021-04-21) 70 | 71 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.6.2...0.6.3) 72 | 73 | * Bugfix: Use slug (normalied header) to decide whether to use a different link anchor 74 | 75 | # v0.6.2 (2021-04-06) 76 | 77 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.6.1...0.6.2) 78 | 79 | * Don't write the TOC if no marker was found 80 | 81 | # v0.6.1 (2021-01-06) 82 | 83 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.6.0...0.6.1) 84 | 85 | * Fix Windows release asset 86 | 87 | # v0.6.0 (2021-01-06) 88 | 89 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.5.1...0.6.0) 90 | 91 | * Generate unique slugs for identically named headers 92 | * Allow custom ToC markers through configuration 93 | * Make maximum header level configurable 94 | 95 | # v0.5.1 (2020-09-28) 96 | 97 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.5.0...0.5.1) 98 | 99 | * Avoid broken compilation in CI 100 | 101 | # v0.5.0 (2020-09-28) 102 | 103 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.4.3...0.5.0) 104 | 105 | * Upgrade dependencies, including mdbook itself to 0.4.3 106 | 107 | # v0.4.3 (2020-06-24) 108 | 109 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.4.2...0.4.3) 110 | 111 | * Start normalizing the levels based on the first header's level 112 | 113 | # v0.4.2 (2020-06-05) 114 | 115 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.4.1...0.4.2) 116 | 117 | * Try to fix indentation levels when header levels have holes 118 | 119 | # v0.4.1 (2020-05-18) 120 | 121 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.4.0...0.4.1) 122 | 123 | * Bug fix: newlines around code blocks were fixed upstream 124 | 125 | # v0.4.0 (2020-05-06) 126 | 127 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.3.0...0.4.0) 128 | 129 | * Combine multiple elements inside a single header 130 | 131 | # v0.3.0 (2020-04-22) 132 | 133 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.2.4...0.3.0) 134 | 135 | * Upgrade dependencies to fix nested HTML/markdown 136 | 137 | # v0.2.4 (2020-04-08) 138 | 139 | [Full changelog](https://github.com/badboy/mdbook-toc/compare/0.2.3...0.2.4) 140 | 141 | * Enable the same markdown extensions as mdbook 142 | -------------------------------------------------------------------------------- /mdbook-toc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mdbook-toc" 3 | version = "0.14.1" 4 | authors = ["Jan-Erik Rediger "] 5 | description = "mdbook preprocessor to add Table of Contents" 6 | license = "MPL-2.0" 7 | homepage = "https://github.com/badboy/mdbook-toc" 8 | repository = "https://github.com/badboy/mdbook-toc" 9 | edition = "2018" 10 | rust-version = "1.66" 11 | 12 | [dependencies] 13 | mdbook = "0.4.33" 14 | pulldown-cmark = "0.9.3" 15 | log = "0.4.19" 16 | clap = { version = "4.3.19", features = ["cargo", "derive"] } 17 | serde_json = "1.0.104" 18 | toml = "0.5.11" 19 | 20 | [dev-dependencies] 21 | pretty_assertions = "1.4.0" 22 | env_logger = "0.10.0" 23 | -------------------------------------------------------------------------------- /mdbook-toc/README.md: -------------------------------------------------------------------------------- 1 | # mdbook-toc 2 | 3 | A preprocessor for [mdbook][] to add inline Table of Contents support. 4 | 5 | [mdbook]: https://github.com/rust-lang-nursery/mdBook 6 | 7 | It turns this marker: 8 | 9 | ```md 10 | 11 | ``` 12 | 13 | into a Table of Contents based on headings of the chapter following the marker. 14 | 15 | ## Installation 16 | 17 | If you want to use only this preprocessor, install the tool: 18 | 19 | ```sh 20 | cargo install mdbook-toc 21 | ``` 22 | 23 | Add it as a preprocessor to your `book.toml`: 24 | 25 | ```toml 26 | [preprocessor.toc] 27 | command = "mdbook-toc" 28 | renderer = ["html"] 29 | ``` 30 | 31 | Finally, build your book as normal: 32 | 33 | ```sh 34 | mdbook path/to/book 35 | ``` 36 | 37 | ## Configuration 38 | 39 | ### Custom TOC marker 40 | 41 | The default marker is: 42 | 43 | ```md 44 | 45 | ``` 46 | 47 | If you wish to use a different marker, such as the GitLab marker `[[_TOC_]]`, you must add the following settings to your `book.toml`. 48 | 49 | ```toml 50 | [preprocessor.toc] 51 | marker = "[[_TOC_]]" 52 | ``` 53 | 54 | You can also use multi-line markers such as the GitHub marker, which is: 55 | 56 | ```md 57 | * auto-gen TOC; 58 | {:toc} 59 | ``` 60 | 61 | Configure the string with a newline: 62 | 63 | ```toml 64 | [preprocessor.toc] 65 | marker = "* auto-gen TOC;\n{:toc}" 66 | ``` 67 | 68 | or with multi-line strings: 69 | 70 | ```toml 71 | [preprocessor.toc] 72 | marker = """* auto-gen TOC; 73 | {:toc}""" 74 | ``` 75 | 76 | ### Maximum header level 77 | 78 | By default the ToC will include headings up to level 4 (`####`). 79 | This can be configured in your `book.toml` as follows: 80 | 81 | ```toml 82 | [preprocessor.toc] 83 | max-level = 4 84 | ``` 85 | 86 | ## License 87 | 88 | MPL. See [LICENSE](LICENSE). 89 | Copyright (c) 2018-2020 Jan-Erik Rediger 90 | -------------------------------------------------------------------------------- /mdbook-toc/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1692799911, 9 | "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "naersk": { 22 | "inputs": { 23 | "nixpkgs": "nixpkgs" 24 | }, 25 | "locked": { 26 | "lastModified": 1692351612, 27 | "narHash": "sha256-KTGonidcdaLadRnv9KFgwSMh1ZbXoR/OBmPjeNMhFwU=", 28 | "owner": "nix-community", 29 | "repo": "naersk", 30 | "rev": "78789c30d64dea2396c9da516bbcc8db3a475207", 31 | "type": "github" 32 | }, 33 | "original": { 34 | "owner": "nix-community", 35 | "repo": "naersk", 36 | "type": "github" 37 | } 38 | }, 39 | "nixpkgs": { 40 | "locked": { 41 | "lastModified": 1692808169, 42 | "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", 43 | "owner": "NixOS", 44 | "repo": "nixpkgs", 45 | "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", 46 | "type": "github" 47 | }, 48 | "original": { 49 | "id": "nixpkgs", 50 | "type": "indirect" 51 | } 52 | }, 53 | "nixpkgs_2": { 54 | "locked": { 55 | "lastModified": 1692808169, 56 | "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", 57 | "owner": "NixOS", 58 | "repo": "nixpkgs", 59 | "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", 60 | "type": "github" 61 | }, 62 | "original": { 63 | "owner": "NixOS", 64 | "ref": "nixpkgs-unstable", 65 | "repo": "nixpkgs", 66 | "type": "github" 67 | } 68 | }, 69 | "root": { 70 | "inputs": { 71 | "flake-utils": "flake-utils", 72 | "naersk": "naersk", 73 | "nixpkgs": "nixpkgs_2" 74 | } 75 | }, 76 | "systems": { 77 | "locked": { 78 | "lastModified": 1681028828, 79 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 80 | "owner": "nix-systems", 81 | "repo": "default", 82 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 83 | "type": "github" 84 | }, 85 | "original": { 86 | "owner": "nix-systems", 87 | "repo": "default", 88 | "type": "github" 89 | } 90 | } 91 | }, 92 | "root": "root", 93 | "version": 7 94 | } 95 | -------------------------------------------------------------------------------- /mdbook-toc/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | flake-utils.url = "github:numtide/flake-utils"; 4 | naersk.url = "github:nix-community/naersk"; 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 | }; 7 | 8 | outputs = { self, flake-utils, naersk, nixpkgs }: 9 | flake-utils.lib.eachDefaultSystem (system: 10 | let 11 | pkgs = (import nixpkgs) { 12 | inherit system; 13 | }; 14 | 15 | naersk' = pkgs.callPackage naersk {}; 16 | 17 | in rec { 18 | # For `nix build` & `nix run`: 19 | defaultPackage = naersk'.buildPackage { 20 | src = ./.; 21 | }; 22 | 23 | # For `nix develop`: 24 | devShell = pkgs.mkShell { 25 | nativeBuildInputs = with pkgs; [ rustc cargo ]; 26 | }; 27 | } 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /mdbook-toc/release.toml: -------------------------------------------------------------------------------- 1 | tag-name = "{{version}}" 2 | -------------------------------------------------------------------------------- /mdbook-toc/src/bin/mdbook-toc.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | extern crate mdbook; 3 | extern crate mdbook_toc; 4 | extern crate serde_json; 5 | 6 | use clap::{Parser, Subcommand}; 7 | use mdbook::errors::Error; 8 | use mdbook::preprocess::{CmdPreprocessor, Preprocessor}; 9 | use mdbook_toc::Toc; 10 | 11 | use std::io; 12 | use std::process; 13 | 14 | #[derive(Parser)] 15 | #[command(about, version)] 16 | struct App { 17 | #[command(subcommand)] 18 | cmd: Option, 19 | } 20 | 21 | #[derive(Subcommand)] 22 | enum Cmd { 23 | /// Check whether a renderer is supported by this preprocessor 24 | Supports { renderer: String }, 25 | } 26 | 27 | fn main() { 28 | let app = App::parse(); 29 | 30 | if let Some(Cmd::Supports { renderer }) = app.cmd { 31 | handle_supports(&renderer); 32 | } else if let Err(e) = handle_preprocessing() { 33 | eprintln!("{e}"); 34 | process::exit(1); 35 | } 36 | } 37 | 38 | fn handle_preprocessing() -> Result<(), Error> { 39 | let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?; 40 | 41 | if ctx.mdbook_version != mdbook::MDBOOK_VERSION { 42 | eprintln!( 43 | "Warning: The mdbook-toc preprocessor was built against version \ 44 | {} of mdbook, but we're being called from version {}", 45 | mdbook::MDBOOK_VERSION, 46 | ctx.mdbook_version 47 | ); 48 | } 49 | 50 | let processed_book = Toc.run(&ctx, book)?; 51 | serde_json::to_writer(io::stdout(), &processed_book)?; 52 | 53 | Ok(()) 54 | } 55 | 56 | fn handle_supports(renderer: &str) -> ! { 57 | let supported = Toc.supports_renderer(renderer); 58 | 59 | // Signal whether the renderer is supported by exiting with 1 or 0. 60 | if supported { 61 | process::exit(0); 62 | } else { 63 | process::exit(1); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /mdbook-toc/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::collections::HashMap; 3 | use std::convert::{TryFrom, TryInto}; 4 | use std::fmt::Write; 5 | 6 | use mdbook::book::{Book, BookItem, Chapter}; 7 | use mdbook::errors::{Error, Result}; 8 | use mdbook::preprocess::{Preprocessor, PreprocessorContext}; 9 | use pulldown_cmark::Tag::*; 10 | use pulldown_cmark::{Event, Options, Parser}; 11 | use toml::value::Table; 12 | 13 | pub struct Toc; 14 | 15 | static DEFAULT_MARKER: &str = "\n"; 16 | 17 | /// Configuration for Table of Contents generation 18 | pub struct Config { 19 | /// Marker to use, defaults to `\n` 20 | pub marker: String, 21 | /// The maximum level of headers to include in the table of contents. 22 | /// Defaults to `4`. 23 | pub max_level: u32, 24 | } 25 | 26 | impl Default for Config { 27 | fn default() -> Config { 28 | Config { 29 | marker: DEFAULT_MARKER.into(), 30 | max_level: 4, 31 | } 32 | } 33 | } 34 | 35 | impl<'a> TryFrom> for Config { 36 | type Error = Error; 37 | 38 | fn try_from(mdbook_cfg: Option<&Table>) -> Result { 39 | let mut cfg = Config::default(); 40 | let mdbook_cfg = match mdbook_cfg { 41 | Some(c) => c, 42 | None => return Ok(cfg), 43 | }; 44 | 45 | if let Some(marker) = mdbook_cfg.get("marker") { 46 | let marker = match marker.as_str() { 47 | Some(m) => m, 48 | None => { 49 | return Err(Error::msg(format!( 50 | "Marker {marker:?} is not a valid string", 51 | ))) 52 | } 53 | }; 54 | cfg.marker = marker.into(); 55 | } 56 | 57 | if let Some(level) = mdbook_cfg.get("max-level") { 58 | let level = match level.as_integer() { 59 | Some(l) => l, 60 | None => { 61 | return Err(Error::msg(format!( 62 | "Level {level:?} is not a valid integer", 63 | ))) 64 | } 65 | }; 66 | cfg.max_level = level.try_into()?; 67 | } 68 | 69 | Ok(cfg) 70 | } 71 | } 72 | 73 | impl Preprocessor for Toc { 74 | fn name(&self) -> &str { 75 | "toc" 76 | } 77 | 78 | fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result { 79 | let mut res = None; 80 | let cfg = ctx.config.get_preprocessor(self.name()).try_into()?; 81 | 82 | book.for_each_mut(|item: &mut BookItem| { 83 | if let Some(Err(_)) = res { 84 | return; 85 | } 86 | 87 | if let BookItem::Chapter(ref mut chapter) = *item { 88 | res = Some(Toc::add_toc(chapter, &cfg).map(|md| { 89 | chapter.content = md; 90 | })); 91 | } 92 | }); 93 | 94 | res.unwrap_or(Ok(())).map(|_| book) 95 | } 96 | } 97 | 98 | fn build_toc(toc: &[(u32, String, String)]) -> String { 99 | log::trace!("ToC from {toc:?}"); 100 | let mut result = String::new(); 101 | 102 | // "Normalize" header levels. 103 | // If headers skip a level, we need to normalize them to avoid the skip. 104 | // Otherwise the markdown render will escape nested levels. 105 | // 106 | // This is a rough approximation only. 107 | let mut toc_iter = toc.iter().peekable(); 108 | 109 | // Start from the level of the first header. 110 | let min_level = toc.iter().map(|(lvl, _, _)| *lvl).min().unwrap_or(1); 111 | let mut last_lower = match toc_iter.peek() { 112 | Some((lvl, _, _)) => *lvl, 113 | None => 0, 114 | }; 115 | let toc = toc.iter().map(|(lvl, name, slug)| { 116 | let lvl = *lvl; 117 | let lvl = match (last_lower + 1).cmp(&lvl) { 118 | Ordering::Less => last_lower + 1, 119 | _ => { 120 | last_lower = lvl; 121 | lvl 122 | } 123 | }; 124 | (lvl, name, slug) 125 | }); 126 | 127 | for (level, name, slug) in toc { 128 | let width = 2 * (level - min_level) as usize; 129 | writeln!(result, "{:width$}* [{name}](#{slug})", "").unwrap(); 130 | } 131 | 132 | result 133 | } 134 | 135 | fn add_toc(content: &str, cfg: &Config) -> Result { 136 | let mut toc_found = false; 137 | 138 | let mut toc_content = vec![]; 139 | let mut current_header = String::new(); 140 | let mut current_header_level: Option = None; 141 | let mut id_counter = HashMap::new(); 142 | 143 | let opts = Options::ENABLE_TABLES 144 | | Options::ENABLE_FOOTNOTES 145 | | Options::ENABLE_STRIKETHROUGH 146 | | Options::ENABLE_TASKLISTS 147 | | Options::ENABLE_HEADING_ATTRIBUTES; 148 | 149 | let mark: Vec = Parser::new(&cfg.marker).collect(); 150 | log::trace!("Marker: {mark:?}"); 151 | let mut mark_start = None; 152 | let mut mark_end = 0..0; 153 | let mut mark_loc = 0; 154 | 155 | let content = content.replace("\r\n", "\n"); 156 | for (e, span) in Parser::new_ext(&content, opts).into_offset_iter() { 157 | log::trace!("Event: {e:?} (span: {span:?})"); 158 | if !toc_found { 159 | log::trace!("TOC not found yet. Location: {mark_loc}, Start: {mark_start:?}"); 160 | if e == mark[mark_loc] { 161 | if mark_start.is_none() { 162 | mark_start = Some(span.clone()); 163 | } 164 | mark_loc += 1; 165 | if mark_loc >= mark.len() { 166 | mark_end = span; 167 | toc_found = true 168 | } 169 | } else if mark_loc > 0 { 170 | mark_loc = 0; 171 | mark_start = None; 172 | } else { 173 | continue; 174 | } 175 | } 176 | 177 | if let Event::Start(Heading(lvl, fragment, classes)) = e { 178 | log::trace!("Header(lvl={lvl}, fragment={fragment:?}, classes={classes:?})"); 179 | current_header_level = Some(lvl as u32); 180 | continue; 181 | } 182 | if let Event::End(Heading(_, fragment, _)) = e { 183 | // Skip if this header is nested too deeply. 184 | if let Some(level) = current_header_level.take() { 185 | let header = current_header.clone(); 186 | let slug = if let Some(slug) = fragment { 187 | // If a fragment is defined, take it as is, not trying to append an extra ID 188 | // in case of duplicates (same behavior as mdBook) 189 | slug.to_owned() 190 | } else { 191 | let mut slug = mdbook::utils::normalize_id(&header); 192 | let id_count = id_counter.entry(slug.clone()).or_insert(0); 193 | 194 | // Append unique ID if multiple headers with the same name exist 195 | // to follow what mdBook does 196 | if *id_count > 0 { 197 | write!(slug, "-{id_count}").unwrap(); 198 | } 199 | 200 | *id_count += 1; 201 | slug 202 | }; 203 | 204 | if level <= cfg.max_level { 205 | toc_content.push((level, header, slug)); 206 | } 207 | 208 | current_header.clear(); 209 | } 210 | continue; 211 | } 212 | if current_header_level.is_none() { 213 | continue; 214 | } 215 | 216 | match e { 217 | Event::Text(header) => write!(current_header, "{header}").unwrap(), 218 | Event::Code(code) => write!(current_header, "`{code}`").unwrap(), 219 | _ => {} // Rest is unhandled 220 | } 221 | } 222 | 223 | let toc = build_toc(&toc_content); 224 | log::trace!("Built TOC: {toc:?}"); 225 | log::trace!("toc_found={toc_found} mark_start={mark_start:?} mark_end={mark_end:?}"); 226 | 227 | let content = if toc_found { 228 | let mark_start = mark_start.unwrap(); 229 | let content_before_toc = &content[0..mark_start.start]; 230 | let content_after_toc = &content[mark_end.end..]; 231 | log::trace!("content_before_toc={content_before_toc:?}"); 232 | log::trace!("content_after_toc={content_after_toc:?}"); 233 | // Multiline markers might have consumed trailing newlines, 234 | // we ensure there's always one before the content. 235 | let extra = if content_after_toc.is_empty() || content_after_toc.as_bytes()[0] == b'\n' { 236 | "" 237 | } else { 238 | "\n" 239 | }; 240 | format!("{content_before_toc}{toc}{extra}{content_after_toc}") 241 | } else { 242 | content.to_string() 243 | }; 244 | 245 | Ok(content) 246 | } 247 | 248 | impl Toc { 249 | /// Add a table of contents to the given chapter. 250 | pub fn add_toc(chapter: &Chapter, cfg: &Config) -> Result { 251 | add_toc(&chapter.content, cfg) 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /mdbook-toc/tests/adds_toc.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | # Header 1 6 | 7 | ## Header 1.1 8 | 9 | # Header 2 10 | 11 | ## Header 2.1 12 | 13 | ## Header 2.2 14 | 15 | ### Header 2.2.1 16 | 17 | -------------------------------------------------------------------------------- /mdbook-toc/tests/adds_toc.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [Header 1.1](#header-11) 5 | * [Header 2](#header-2) 6 | * [Header 2.1](#header-21) 7 | * [Header 2.2](#header-22) 8 | * [Header 2.2.1](#header-221) 9 | 10 | # Header 1 11 | 12 | ## Header 1.1 13 | 14 | # Header 2 15 | 16 | ## Header 2.1 17 | 18 | ## Header 2.2 19 | 20 | ### Header 2.2.1 21 | 22 | -------------------------------------------------------------------------------- /mdbook-toc/tests/attributes.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | # Header 1 6 | 7 | ## Header {#header-1-sub} 8 | 9 | # Header 2 10 | 11 | ## Header {#header-2-sub .class1 .class2} 12 | -------------------------------------------------------------------------------- /mdbook-toc/tests/attributes.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [Header](#header-1-sub) 5 | * [Header 2](#header-2) 6 | * [Header](#header-2-sub) 7 | 8 | # Header 1 9 | 10 | ## Header {#header-1-sub} 11 | 12 | # Header 2 13 | 14 | ## Header {#header-2-sub .class1 .class2} 15 | -------------------------------------------------------------------------------- /mdbook-toc/tests/backslash_escapes.in.md: -------------------------------------------------------------------------------- 1 | \*not emphasized* 2 | \
not a tag 3 | \[not a link](/foo) 4 | \`not code` 5 | \* not a list 6 | \# not a heading 7 | \[foo]: /url "not a reference" 8 | \ö not a character entity 9 | 1\. not a list 10 | -------------------------------------------------------------------------------- /mdbook-toc/tests/backslash_escapes.out.md: -------------------------------------------------------------------------------- 1 | \*not emphasized* 2 | \
not a tag 3 | \[not a link](/foo) 4 | \`not code` 5 | \* not a list 6 | \# not a heading 7 | \[foo]: /url "not a reference" 8 | \ö not a character entity 9 | 1\. not a list 10 | -------------------------------------------------------------------------------- /mdbook-toc/tests/crlf.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | # Header 1 6 | 7 | ## Header 1.1 8 | 9 | # Header 2 10 | 11 | ## Header 2.1 12 | 13 | ## Header 2.2 14 | 15 | ### Header 2.2.1 16 | 17 | -------------------------------------------------------------------------------- /mdbook-toc/tests/crlf.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [Header 1.1](#header-11) 5 | * [Header 2](#header-2) 6 | * [Header 2.1](#header-21) 7 | * [Header 2.2](#header-22) 8 | * [Header 2.2.1](#header-221) 9 | 10 | # Header 1 11 | 12 | ## Header 1.1 13 | 14 | # Header 2 15 | 16 | ## Header 2.1 17 | 18 | ## Header 2.2 19 | 20 | ### Header 2.2.1 21 | 22 | -------------------------------------------------------------------------------- /mdbook-toc/tests/empty_document.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | 3 | 4 | -------------------------------------------------------------------------------- /mdbook-toc/tests/empty_document.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | 3 | -------------------------------------------------------------------------------- /mdbook-toc/tests/github_marker.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * auto-gen TOC: 4 | {:toc} 5 | 6 | # Header 1 7 | 8 | ## Header 1.1 9 | 10 | # Header 2 11 | 12 | ## Header 2.1 13 | 14 | ## Header 2.2 15 | 16 | ### Header 2.2.1 17 | 18 | -------------------------------------------------------------------------------- /mdbook-toc/tests/github_marker.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [Header 1.1](#header-11) 5 | * [Header 2](#header-2) 6 | * [Header 2.1](#header-21) 7 | * [Header 2.2](#header-22) 8 | * [Header 2.2.1](#header-221) 9 | 10 | # Header 1 11 | 12 | ## Header 1.1 13 | 14 | # Header 2 15 | 16 | ## Header 2.1 17 | 18 | ## Header 2.2 19 | 20 | ### Header 2.2.1 21 | 22 | -------------------------------------------------------------------------------- /mdbook-toc/tests/gitlab_marker.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | [[_TOC_]] 4 | 5 | # Header 1 6 | 7 | ## Header 1.1 8 | 9 | # Header 2 10 | 11 | ## Header 2.1 12 | 13 | ## Header 2.2 14 | 15 | ### Header 2.2.1 16 | 17 | -------------------------------------------------------------------------------- /mdbook-toc/tests/gitlab_marker.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [Header 1.1](#header-11) 5 | * [Header 2](#header-2) 6 | * [Header 2.1](#header-21) 7 | * [Header 2.2](#header-22) 8 | * [Header 2.2.1](#header-221) 9 | 10 | # Header 1 11 | 12 | ## Header 1.1 13 | 14 | # Header 2 15 | 16 | ## Header 2.1 17 | 18 | ## Header 2.2 19 | 20 | ### Header 2.2.1 21 | 22 | -------------------------------------------------------------------------------- /mdbook-toc/tests/handles_inline_code.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | # Header 1 6 | 7 | ## Header 1.1 8 | 9 | ### Header 1.1.1 10 | 11 | #### Header 1.1.1.1 12 | 13 | ##### Header 1.1.1.1.1 14 | 15 | # Another header `with inline` code 16 | -------------------------------------------------------------------------------- /mdbook-toc/tests/handles_inline_code.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [Header 1.1](#header-11) 5 | * [Header 1.1.1](#header-111) 6 | * [Header 1.1.1.1](#header-1111) 7 | * [Another header `with inline` code](#another-header-with-inline-code) 8 | 9 | # Header 1 10 | 11 | ## Header 1.1 12 | 13 | ### Header 1.1.1 14 | 15 | #### Header 1.1.1.1 16 | 17 | ##### Header 1.1.1.1.1 18 | 19 | # Another header `with inline` code 20 | -------------------------------------------------------------------------------- /mdbook-toc/tests/higher_max_level.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | # Header 1 6 | 7 | ## Header 1.1 8 | 9 | # Header 2 10 | 11 | ## Header 2.1 12 | 13 | ## Header 2.2 14 | 15 | ### Header 2.2.1 16 | 17 | -------------------------------------------------------------------------------- /mdbook-toc/tests/higher_max_level.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [Header 1.1](#header-11) 5 | * [Header 2](#header-2) 6 | * [Header 2.1](#header-21) 7 | * [Header 2.2](#header-22) 8 | * [Header 2.2.1](#header-221) 9 | 10 | # Header 1 11 | 12 | ## Header 1.1 13 | 14 | # Header 2 15 | 16 | ## Header 2.1 17 | 18 | ## Header 2.2 19 | 20 | ### Header 2.2.1 21 | 22 | -------------------------------------------------------------------------------- /mdbook-toc/tests/it.rs: -------------------------------------------------------------------------------- 1 | use mdbook::book::Chapter; 2 | use mdbook_toc::{Config, Toc}; 3 | use pretty_assertions::assert_eq; 4 | 5 | fn default() -> T { 6 | Default::default() 7 | } 8 | 9 | fn with_marker>(marker: S) -> Config { 10 | Config { 11 | marker: marker.into(), 12 | ..Config::default() 13 | } 14 | } 15 | 16 | fn with_max_level(level: u32) -> Config { 17 | Config { 18 | max_level: level, 19 | ..Config::default() 20 | } 21 | } 22 | 23 | trait FromContent { 24 | fn from_content(content: String) -> Self; 25 | } 26 | 27 | impl FromContent for Chapter { 28 | fn from_content(content: String) -> Self { 29 | Self { 30 | name: "chapter".into(), 31 | content, 32 | number: None, 33 | sub_items: vec![], 34 | path: None, 35 | source_path: None, 36 | parent_names: vec![], 37 | } 38 | } 39 | } 40 | 41 | /// Assert the Table of Content generation for an input file against the expected output file. 42 | /// 43 | /// Reads `tests/$name.in.md` and checks the generated ToC code against `tests/$name.out.md`. 44 | macro_rules! assert_toc { 45 | ($name:expr) => { 46 | assert_toc!($name, default()) 47 | }; 48 | ($name:expr, $config:expr) => { 49 | let _ = env_logger::builder().is_test(true).try_init(); 50 | 51 | let config = $config; 52 | let content = ::std::fs::read_to_string(format!("tests/{}.in.md", $name)).expect(concat!( 53 | "Can't read ", 54 | $name, 55 | ".in.md" 56 | )); 57 | let expected = ::std::fs::read_to_string(format!("tests/{}.out.md", $name)) 58 | .expect(concat!("Can't read ", $name, ".out.md")); 59 | 60 | let chapter = Chapter::from_content(content); 61 | let result = Toc::add_toc(&chapter, &config); 62 | match result { 63 | Ok(result) => assert_eq!(expected, result), 64 | Err(e) => panic!("{} failed. Error: {}", $name, e), 65 | } 66 | }; 67 | } 68 | 69 | #[test] 70 | fn adds_toc() { 71 | assert_toc!("adds_toc", default()); 72 | } 73 | 74 | #[test] 75 | fn adds_toc_with_inline_code() { 76 | assert_toc!("with_inline_code", default()); 77 | } 78 | 79 | #[test] 80 | fn leaves_tables_untouched() { 81 | // Regression test. 82 | // Previously we forgot to enable the same markdwon extensions as mdbook itself. 83 | // Markdown roundtripping removes some insignificant whitespace 84 | assert_toc!("tables_untouched"); 85 | } 86 | 87 | #[test] 88 | fn handles_inline_code() { 89 | // Regression test. 90 | // Inline code in a header was broken up into multiple items. 91 | // Also test for deeply nested headers. 92 | assert_toc!("handles_inline_code"); 93 | } 94 | 95 | #[test] 96 | fn multi_header_regression() { 97 | assert_toc!("multi_header"); 98 | } 99 | 100 | #[test] 101 | fn multi_header_linear_regression_3() { 102 | assert_toc!("multi_header_linear"); 103 | } 104 | 105 | #[test] 106 | fn add_toc_with_gitlab_marker() { 107 | let marker = "[[_TOC_]]".to_owned(); 108 | 109 | assert_toc!("gitlab_marker", with_marker(marker)); 110 | } 111 | 112 | #[test] 113 | fn unique_slugs() { 114 | assert_toc!("unique_slugs"); 115 | } 116 | 117 | #[test] 118 | fn add_toc_with_github_marker() { 119 | let marker = "* auto-gen TOC:\n{:toc}\n".to_owned(); 120 | assert_toc!("github_marker", with_marker(marker)); 121 | } 122 | 123 | #[test] 124 | fn lower_max_level() { 125 | assert_toc!("lower_max_level", with_max_level(2)); 126 | } 127 | 128 | #[test] 129 | fn higher_max_level() { 130 | assert_toc!("higher_max_level", with_max_level(7)); 131 | } 132 | 133 | // Regression test for [#13](https://github.com/badboy/mdbook-toc/issues/13). 134 | // Choosing a non-HTML TOC marker breaks sites that don't use it at all, 135 | // removed the header and first paragraph. 136 | #[test] 137 | fn nonhtml_marker_no_toc_in_page() { 138 | let marker = "[[_TOC_]]".to_owned(); 139 | assert_toc!("nonhtml_marker_no_use", with_marker(marker)); 140 | } 141 | 142 | #[test] 143 | fn similar_heading_different_casing() { 144 | // Regression test #15 145 | // Previously we didn't use the normalized header ("slug") to decide whether to use 146 | // different link anchors. 147 | 148 | assert_toc!("similar_heading_different_casing"); 149 | } 150 | 151 | #[test] 152 | fn tables_with_html() { 153 | assert_toc!("tables_with_html"); 154 | } 155 | 156 | #[test] 157 | fn backslash_escapes() { 158 | // Regression test #21 159 | // Backslash-escaped elements should still be escaped. 160 | assert_toc!("backslash_escapes"); 161 | } 162 | 163 | #[test] 164 | fn empty_document() { 165 | // Regression test #31 166 | // Empty documents should not fail 167 | assert_toc!("empty_document"); 168 | } 169 | 170 | #[test] 171 | fn crlf() { 172 | assert_toc!("crlf"); 173 | } 174 | 175 | #[test] 176 | fn attributes() { 177 | assert_toc!("attributes"); 178 | } 179 | -------------------------------------------------------------------------------- /mdbook-toc/tests/lower_max_level.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | # Header 1 6 | 7 | ## Header 1.1 8 | 9 | # Header 2 10 | 11 | ## Header 2.1 12 | 13 | ## Header 2.2 14 | 15 | ### Header 2.2.1 16 | 17 | -------------------------------------------------------------------------------- /mdbook-toc/tests/lower_max_level.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [Header 1.1](#header-11) 5 | * [Header 2](#header-2) 6 | * [Header 2.1](#header-21) 7 | * [Header 2.2](#header-22) 8 | 9 | # Header 1 10 | 11 | ## Header 1.1 12 | 13 | # Header 2 14 | 15 | ## Header 2.1 16 | 17 | ## Header 2.2 18 | 19 | ### Header 2.2.1 20 | 21 | -------------------------------------------------------------------------------- /mdbook-toc/tests/multi_header.in.md: -------------------------------------------------------------------------------- 1 | # Main Summary 2 | 3 | 4 | 5 | # Introduction 6 | 7 | ### Contents 8 | 9 | ### Background and Caveats 10 | 11 | #### Test 12 | 13 | ### Accessing the Data 14 | 15 | # Adding New Fields 16 | 17 | ## User Preferences 18 | -------------------------------------------------------------------------------- /mdbook-toc/tests/multi_header.out.md: -------------------------------------------------------------------------------- 1 | # Main Summary 2 | 3 | * [Introduction](#introduction) 4 | * [Contents](#contents) 5 | * [Background and Caveats](#background-and-caveats) 6 | * [Test](#test) 7 | * [Accessing the Data](#accessing-the-data) 8 | * [Adding New Fields](#adding-new-fields) 9 | * [User Preferences](#user-preferences) 10 | 11 | # Introduction 12 | 13 | ### Contents 14 | 15 | ### Background and Caveats 16 | 17 | #### Test 18 | 19 | ### Accessing the Data 20 | 21 | # Adding New Fields 22 | 23 | ## User Preferences 24 | -------------------------------------------------------------------------------- /mdbook-toc/tests/multi_header_linear.in.md: -------------------------------------------------------------------------------- 1 | # Heading 2 | 3 | 4 | 5 | ## Level 1.1 6 | ### Level 1.1.1 7 | ### Level 1.1.2 8 | ## Level 1.2 9 | ### Level 1.2.1 10 | 11 | text 12 | -------------------------------------------------------------------------------- /mdbook-toc/tests/multi_header_linear.out.md: -------------------------------------------------------------------------------- 1 | # Heading 2 | 3 | * [Level 1.1](#level-11) 4 | * [Level 1.1.1](#level-111) 5 | * [Level 1.1.2](#level-112) 6 | * [Level 1.2](#level-12) 7 | * [Level 1.2.1](#level-121) 8 | 9 | ## Level 1.1 10 | ### Level 1.1.1 11 | ### Level 1.1.2 12 | ## Level 1.2 13 | ### Level 1.2.1 14 | 15 | text 16 | -------------------------------------------------------------------------------- /mdbook-toc/tests/nonhtml_marker_no_use.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | First paragraph 4 | 5 | # Header 1 6 | 7 | ## Header 1.1 8 | 9 | # Header 2 10 | 11 | ## Header 2.1 12 | 13 | ## Header 2.2 14 | 15 | ### Header 2.2.1 16 | 17 | -------------------------------------------------------------------------------- /mdbook-toc/tests/nonhtml_marker_no_use.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | First paragraph 4 | 5 | # Header 1 6 | 7 | ## Header 1.1 8 | 9 | # Header 2 10 | 11 | ## Header 2.1 12 | 13 | ## Header 2.2 14 | 15 | ### Header 2.2.1 16 | 17 | -------------------------------------------------------------------------------- /mdbook-toc/tests/similar_heading_different_casing.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | # Tag 6 | 7 | ## tag 8 | 9 | -------------------------------------------------------------------------------- /mdbook-toc/tests/similar_heading_different_casing.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Tag](#tag) 4 | * [tag](#tag-1) 5 | 6 | # Tag 7 | 8 | ## tag 9 | 10 | -------------------------------------------------------------------------------- /mdbook-toc/tests/tables_untouched.in.md: -------------------------------------------------------------------------------- 1 | # Heading 2 | 3 | | Head 1 | Head 2 | 4 | |--------|--------| 5 | | Row 1 | Row 2 | 6 | 7 | -------------------------------------------------------------------------------- /mdbook-toc/tests/tables_untouched.out.md: -------------------------------------------------------------------------------- 1 | # Heading 2 | 3 | | Head 1 | Head 2 | 4 | |--------|--------| 5 | | Row 1 | Row 2 | 6 | 7 | -------------------------------------------------------------------------------- /mdbook-toc/tests/tables_with_html.in.md: -------------------------------------------------------------------------------- 1 | # Heading 2 | 3 | | Head 1 | Head 2 | 4 | |--------|--------| 5 | | Row 1 | Row 2 | 6 | -------------------------------------------------------------------------------- /mdbook-toc/tests/tables_with_html.out.md: -------------------------------------------------------------------------------- 1 | # Heading 2 | 3 | | Head 1 | Head 2 | 4 | |--------|--------| 5 | | Row 1 | Row 2 | 6 | -------------------------------------------------------------------------------- /mdbook-toc/tests/unique_slugs.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | ## Duplicate 6 | 7 | ### Duplicate 8 | 9 | #### Duplicate 10 | 11 | ##### Duplicate 12 | 13 | ## Duplicate 14 | 15 | -------------------------------------------------------------------------------- /mdbook-toc/tests/unique_slugs.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Duplicate](#duplicate) 4 | * [Duplicate](#duplicate-1) 5 | * [Duplicate](#duplicate-2) 6 | * [Duplicate](#duplicate-4) 7 | 8 | ## Duplicate 9 | 10 | ### Duplicate 11 | 12 | #### Duplicate 13 | 14 | ##### Duplicate 15 | 16 | ## Duplicate 17 | 18 | -------------------------------------------------------------------------------- /mdbook-toc/tests/with_inline_code.in.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | 4 | 5 | # Header 1 6 | 7 | ## `Header 1.1` 8 | 9 | # Header 2 10 | 11 | ## Header 2.1 12 | 13 | When code or an identifier must appear in a message or label, it should be 14 | surrounded with backticks: `` `foo.bar` ``. 15 | -------------------------------------------------------------------------------- /mdbook-toc/tests/with_inline_code.out.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 | 3 | * [Header 1](#header-1) 4 | * [`Header 1.1`](#header-11) 5 | * [Header 2](#header-2) 6 | * [Header 2.1](#header-21) 7 | 8 | # Header 1 9 | 10 | ## `Header 1.1` 11 | 12 | # Header 2 13 | 14 | ## Header 2.1 15 | 16 | When code or an identifier must appear in a message or label, it should be 17 | surrounded with backticks: `` `foo.bar` ``. 18 | -------------------------------------------------------------------------------- /mdbook-xnos/mdbook-xnos.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import sys 4 | 5 | r_prefix = re.compile(r'^# (\w+\.)', flags=re.MULTILINE) 6 | # XXX: mdBook doesn't support attributes on arbitrary elements 7 | # r_def1 = re.compile(r'#(fig|tbl|eq|sec):([\w-]+)') # Matches #fig:foo 8 | r_def2 = re.compile(r'id="(fig|tbl|eq|sec):([\w-]+)"') # Matches id="fig:foo" 9 | r_ref1 = re.compile(r'\\?([!+*]?)@(fig|tbl|eq|sec):([\w-]+)') # Matches @fig:foo 10 | # XXX: mdBook seems to escape bare * 11 | r_ref2 = re.compile(r'{\\?([!+*]?)@(fig|tbl|eq|sec):([\w-]+)}') # Matches {@fig:foo} 12 | 13 | def run(section): 14 | # Other book item (part title or separator) 15 | if 'Chapter' not in section: 16 | return 17 | 18 | content = section['Chapter']['content'] 19 | 20 | prefix = '' 21 | 22 | if section['Chapter']['number'] == None: 23 | m = r_prefix.search(content) 24 | if m != None: 25 | prefix = m.group(1) 26 | else: 27 | prefix = str(section['Chapter']['number'][0]) + '.' 28 | 29 | counts = { 'fig': 0, 'tbl': 0, 'eq': 0, 'sec': 0 } 30 | 31 | id_to_num = {} 32 | 33 | def add_id(match): 34 | kind = match.group(1) 35 | id = (kind + ':' + match.group(2)) 36 | counts[kind] += 1 37 | id_to_num[id] = prefix + str(counts[kind]) 38 | 39 | # for match in r_def1.finditer(content): add_id(match) 40 | for match in r_def2.finditer(content): add_id(match) 41 | 42 | plus_names = { 'fig': 'fig', 'tbl': 'table', 'eq': 'eq', 'sec': 'section' } 43 | star_names = { 'fig': 'Fig', 'tbl': 'Table', 'eq': 'Eq', 'sec': 'Section' } 44 | 45 | clever_refs = True 46 | clever_names = plus_names 47 | 48 | def replacefigs(match): 49 | op = match.group(1) 50 | kind = match.group(2) 51 | id = (kind + ':' + match.group(3)) 52 | if id in id_to_num: 53 | num = id_to_num[id] 54 | if clever_refs: 55 | if op == '!': return num 56 | elif op == '+': return plus_names[kind] + ' ' + num 57 | elif op == '*': return star_names[kind] + ' ' + num 58 | else: return clever_names[kind] + ' ' + num 59 | else: 60 | if op == '+': return plus_names[kind] + ' ' + num 61 | elif op == '*': return star_names[kind] + ' ' + num 62 | else: return num 63 | else: 64 | return match.group(0) # fallback, don't do any replacement 65 | 66 | # XXX: dead code in original xnos.py? 67 | # return id_to_num.get(id, fallback) 68 | 69 | content = r_ref2.sub(replacefigs, content) 70 | content = r_ref1.sub(replacefigs, content) 71 | 72 | section['Chapter']['content'] = content 73 | 74 | if __name__ == '__main__': 75 | if len(sys.argv) > 1: # we check if we received any argument 76 | if sys.argv[1] == "supports": 77 | # then we are good to return an exit status code of 0, since the other argument will just be the renderer's name 78 | sys.exit(0) 79 | 80 | # load both the context and the book representations from stdin 81 | context, book = json.load(sys.stdin) 82 | 83 | for chapter in book['sections']: 84 | run(chapter) 85 | 86 | print(json.dumps(book)) 87 | -------------------------------------------------------------------------------- /preproc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pandocs-preproc" 3 | version = "0.1.0" 4 | authors = ["ISSOtm "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1.0.42" 11 | clap = "2.33.3" 12 | lazy_static = "1.4.0" 13 | # mdbook here is only used as a lib, so no need for the extra features 14 | mdbook = { version = "0.4.8", default-features = false } 15 | pulldown-cmark = "0.8.0" 16 | pulldown-cmark-to-cmark = "6.0.1" 17 | regex = "1.5.4" 18 | serde_json = "1.0.59" 19 | termcolor = "1.1.2" 20 | -------------------------------------------------------------------------------- /preproc/src/admonitions.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Adapted from gbdev's Pan Docs preprocessor 3 | * https://github.com/gbdev/pandocs/blob/master/preproc/src/admonitions.rs 4 | * 5 | * This Source Code Form is subject to the 6 | * terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not 8 | * distributed with this file, You can 9 | * obtain one at 10 | * http://mozilla.org/MPL/2.0/. 11 | */ 12 | 13 | use std::{iter::Peekable, matches}; 14 | 15 | use anyhow::Error; 16 | use mdbook::book::Chapter; 17 | use pulldown_cmark::{Event, Options, Parser, Tag}; 18 | 19 | use crate::Pandocs; 20 | 21 | impl Pandocs { 22 | pub fn process_admonitions(&self, chapter: &mut Chapter) -> Result<(), Error> { 23 | let mut buf = String::with_capacity(chapter.content.len()); 24 | let extensions = 25 | Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES | Options::ENABLE_STRIKETHROUGH; 26 | 27 | let events = AdmonitionsGenerator::new(Parser::new_ext(&chapter.content, extensions)); 28 | 29 | pulldown_cmark_to_cmark::cmark(events, &mut buf, None) 30 | .map_err(|err| Error::from(err).context("Markdown serialization failed"))?; 31 | chapter.content = buf; 32 | 33 | Ok(()) 34 | } 35 | } 36 | 37 | struct AdmonitionsGenerator<'a, Iter: Iterator>> { 38 | iter: Peekable, 39 | nesting_level: usize, 40 | at_paragraph_start: bool, 41 | } 42 | 43 | impl<'a, Iter: Iterator>> AdmonitionsGenerator<'a, Iter> { 44 | const KINDS: [&'static str; 4] = ["note", "tip", "warning", "danger"]; 45 | 46 | fn new(iter: Iter) -> Self { 47 | Self { 48 | iter: iter.peekable(), 49 | nesting_level: 0, 50 | at_paragraph_start: false, 51 | } 52 | } 53 | } 54 | 55 | impl<'a, Iter: Iterator>> Iterator for AdmonitionsGenerator<'a, Iter> { 56 | type Item = Event<'a>; 57 | 58 | fn next(&mut self) -> Option { 59 | let mut evt = self.iter.next()?; 60 | 61 | match evt { 62 | Event::Text(ref text) if self.at_paragraph_start => { 63 | if let Some(params) = text.strip_prefix(":::") { 64 | // Check that there is no more text in the paragraph; if there isn't, we'll consume the entire paragraph. 65 | // Note that this intentionally rejects any formatting within the paragraph—serialisation would be too complex. 66 | if matches!(self.iter.peek(), Some(Event::End(Tag::Paragraph))) { 67 | if params.is_empty() { 68 | if self.nesting_level != 0 { 69 | // Ending an admonition. 70 | self.nesting_level -= 1; 71 | 72 | evt = Event::Html("".into()); 73 | } 74 | } else { 75 | let (kind, title) = 76 | match params.split_once(|c: char| c.is_ascii_whitespace()) { 77 | Some((kind, title)) => (kind, title.trim()), 78 | None => (params, ""), 79 | }; 80 | if Self::KINDS.contains(&kind) { 81 | // Beginning an admonition. 82 | self.nesting_level += 1; 83 | 84 | evt = Event::Html( 85 | if title.is_empty() { 86 | format!("
") 87 | } else { 88 | format!("

{title}

") 89 | } 90 | .into(), 91 | ); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | _ => {} 98 | } 99 | 100 | self.at_paragraph_start = matches!(evt, Event::Start(Tag::Paragraph)); 101 | 102 | Some(evt) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /preproc/src/git.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Adapted from gbdev's Pan Docs preprocessor 3 | * https://github.com/gbdev/pandocs/blob/master/preproc/src/git.rs 4 | * 5 | * This Source Code Form is subject to the 6 | * terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not 8 | * distributed with this file, You can 9 | * obtain one at 10 | * http://mozilla.org/MPL/2.0/. 11 | */ 12 | 13 | use std::process::{Command, Stdio}; 14 | 15 | use anyhow::Error; 16 | 17 | #[derive(Debug)] 18 | pub struct Commit { 19 | info: String, 20 | splits: [usize; 2], 21 | } 22 | 23 | impl Commit { 24 | pub fn rev_parse(what: &str) -> Result { 25 | let output = Command::new("git") 26 | .args(["show", "-s", "--format=%H%x00%h%x00%ci", what]) 27 | .stderr(Stdio::inherit()) 28 | .stdin(Stdio::null()) 29 | .output() 30 | .expect("Failed to get commit info"); 31 | if !output.status.success() { 32 | return Err(Error::msg(format!( 33 | "Git exited with status {} while getting commit info", 34 | output.status 35 | ))); 36 | } 37 | let info = String::from_utf8(output.stdout).expect("Commit info is not valid UTF-8??"); 38 | 39 | let first_split = info 40 | .find('\0') 41 | .expect("Failed to split hash and short hash"); 42 | let second_split = info[first_split + 1..] 43 | .find('\0') 44 | .expect("Failed to split short hash and timestamp") 45 | + first_split; 46 | 47 | Ok(Self { 48 | info, 49 | splits: [first_split, second_split], 50 | }) 51 | } 52 | 53 | pub fn hash(&self) -> &str { 54 | &self.info[..self.splits[0]] 55 | } 56 | 57 | pub fn short_hash(&self) -> &str { 58 | &self.info[self.splits[0] + 1..self.splits[1]] 59 | } 60 | 61 | pub fn timestamp(&self) -> &str { 62 | &self.info[self.splits[1] + 1..] 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /preproc/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Adapted from gbdev's Pan Docs preprocessor 3 | * https://github.com/gbdev/pandocs/blob/master/preproc/src/main.rs 4 | * 5 | * This Source Code Form is subject to the 6 | * terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not 8 | * distributed with this file, You can 9 | * obtain one at 10 | * http://mozilla.org/MPL/2.0/. 11 | */ 12 | 13 | use std::io; 14 | use std::process; 15 | 16 | use clap::{App, Arg, ArgMatches, SubCommand}; 17 | use mdbook::book::Book; 18 | use mdbook::errors::Error; 19 | use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext}; 20 | use mdbook::BookItem; 21 | 22 | mod admonitions; 23 | mod git; 24 | use git::Commit; 25 | 26 | pub fn make_app() -> App<'static, 'static> { 27 | App::new("pandocs-preproc") 28 | .about("A mdbook preprocessor for Pan Docs") 29 | .subcommand( 30 | SubCommand::with_name("supports") 31 | .arg(Arg::with_name("renderer").required(true)) 32 | .about("Check whether a renderer is supported by this preprocessor"), 33 | ) 34 | } 35 | 36 | fn main() -> Result<(), Error> { 37 | let matches = make_app().get_matches(); 38 | 39 | // Users will want to construct their own preprocessor here 40 | let preprocessor = Pandocs::new(); 41 | 42 | if let Some(sub_args) = matches.subcommand_matches("supports") { 43 | handle_supports(&preprocessor, sub_args); 44 | } else { 45 | handle_preprocessing(&preprocessor) 46 | } 47 | } 48 | 49 | fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> { 50 | let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?; 51 | 52 | if ctx.mdbook_version != mdbook::MDBOOK_VERSION { 53 | // We should probably use the `semver` crate to check compatibility 54 | // here... 55 | eprintln!( 56 | "Warning: The {} plugin was built against version {} of mdbook, \ 57 | but we're being called from version {}", 58 | pre.name(), 59 | mdbook::MDBOOK_VERSION, 60 | ctx.mdbook_version 61 | ); 62 | } 63 | 64 | let processed_book = pre.run(&ctx, book)?; 65 | serde_json::to_writer(io::stdout(), &processed_book)?; 66 | 67 | Ok(()) 68 | } 69 | 70 | fn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! { 71 | let renderer = sub_args.value_of("renderer").expect("Required argument"); 72 | let supported = pre.supports_renderer(renderer); 73 | 74 | // Signal whether the renderer is supported by exiting with 1 or 0. 75 | if supported { 76 | process::exit(0); 77 | } else { 78 | process::exit(1); 79 | } 80 | } 81 | 82 | struct Pandocs; 83 | 84 | impl Pandocs { 85 | fn new() -> Pandocs { 86 | Pandocs 87 | } 88 | } 89 | 90 | impl Preprocessor for Pandocs { 91 | fn name(&self) -> &str { 92 | "pandocs-preproc" 93 | } 94 | 95 | fn supports_renderer(&self, renderer: &str) -> bool { 96 | renderer != "not-supported" 97 | } 98 | 99 | fn run(&self, _: &PreprocessorContext, mut book: Book) -> Result { 100 | 101 | 102 | let mut res = Ok(()); 103 | 104 | book.for_each_mut(|item| { 105 | macro_rules! abort_if_err { 106 | ($expr:expr) => { 107 | match $expr { 108 | Err(e) => { 109 | res = Err(e); 110 | return; 111 | } 112 | Ok(ret) => ret, 113 | } 114 | }; 115 | } 116 | 117 | if res.is_err() { 118 | return; 119 | } 120 | 121 | if let BookItem::Chapter(ref mut chapter) = item { 122 | abort_if_err!(self.process_admonitions(chapter)); 123 | 124 | if chapter.name == "Foreword" { 125 | let commit = abort_if_err!(Commit::rev_parse("HEAD")); 126 | chapter.content.push_str(&format!( 127 | "This document version was produced from git commit
{} ({}). ", 128 | commit.hash(), commit.short_hash(), commit.timestamp(), 129 | )); 130 | } 131 | } 132 | }); 133 | 134 | res.map(|_| book) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /theme/css/general.css: -------------------------------------------------------------------------------- 1 | /* Base styles and content styles */ 2 | 3 | @import '../fonts/fira_code.css'; 4 | @import 'variables.css'; 5 | 6 | @font-face { 7 | font-family: 'SourceSans3'; 8 | src: url('../fonts/SourceSans3-Regular.otf.woff') format('woff'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | @font-face { 14 | font-family: 'SourceSans3'; 15 | src: url('../fonts/SourceSans3-It.otf.woff') format('woff'); 16 | font-weight: normal; 17 | font-style: italic; 18 | } 19 | 20 | @font-face { 21 | font-family: 'SourceSans3'; 22 | src: url('../fonts/SourceSans3-Semibold.otf.woff') format('woff'); 23 | font-weight: bold; 24 | font-style: normal; 25 | } 26 | 27 | @font-face { 28 | font-family: 'SourceSans3'; 29 | src: url('../fonts/SourceSans3-SemiboldIt.otf.woff') format('woff'); 30 | font-weight: bold; 31 | font-style: italic; 32 | } 33 | 34 | :root { 35 | /* Browser default font-size is 16px, this way 1 rem = 10px */ 36 | font-size: 62.5%; 37 | color-scheme: var(--color-scheme); 38 | /* TODO: Why is important needed here? */ 39 | --mono-font: "Fira Code" !important; 40 | --mono-font-size: 1.4rem; 41 | } 42 | 43 | .sidebar { 44 | font-size: 1.6rem !important; 45 | } 46 | math { 47 | font-size: 1.9rem; 48 | } 49 | 50 | html { 51 | font-family: 'SourceSans3', sans-serif; 52 | color: var(--fg); 53 | background-color: var(--bg); 54 | text-size-adjust: none; 55 | -webkit-text-size-adjust: none; 56 | } 57 | 58 | body { 59 | margin: 0; 60 | font-size: 1.8rem; 61 | font-weight: 400; 62 | overflow-x: hidden; 63 | } 64 | 65 | code { 66 | font-family: var(--mono-font) !important; 67 | font-weight: 500; 68 | font-size: var(--mono-font-size); 69 | direction: ltr !important; 70 | font-variant-ligatures: none; 71 | } 72 | 73 | /* make long words/inline code not x overflow */ 74 | main { 75 | overflow-wrap: break-word; 76 | } 77 | 78 | /* make wide tables scroll if they overflow */ 79 | .table-wrapper { 80 | overflow-x: auto; 81 | } 82 | 83 | /* Don't change font size in headers. */ 84 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 85 | font-size: unset; 86 | } 87 | 88 | .left { float: left; } 89 | .right { float: right; } 90 | .boring { opacity: 0.6; } 91 | .hide-boring .boring { display: none; } 92 | .hidden { display: none !important; } 93 | 94 | h2, h3 { margin-block-start: 2.5em; } 95 | h4, h5 { margin-block-start: 2em; } 96 | 97 | .header + .header h3, 98 | .header + .header h4, 99 | .header + .header h5 { 100 | margin-block-start: 1em; 101 | } 102 | 103 | h1:target::before, 104 | h2:target::before, 105 | h3:target::before, 106 | h4:target::before, 107 | h5:target::before, 108 | h6:target::before { 109 | display: inline-block; 110 | content: "»"; 111 | margin-inline-start: -30px; 112 | width: 30px; 113 | } 114 | 115 | /* This is broken on Safari as of version 14, but is fixed 116 | in Safari Technology Preview 117 which I think will be Safari 14.2. 117 | https://bugs.webkit.org/show_bug.cgi?id=218076 118 | */ 119 | :target { 120 | /* Safari does not support logical properties */ 121 | scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); 122 | } 123 | 124 | .page { 125 | outline: 0; 126 | padding: 0 var(--page-padding); 127 | margin-block-start: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ 128 | } 129 | .page-wrapper { 130 | box-sizing: border-box; 131 | background-color: var(--bg); 132 | } 133 | .no-js .page-wrapper, 134 | .js:not(.sidebar-resizing) .page-wrapper { 135 | transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ 136 | } 137 | [dir=rtl] .js:not(.sidebar-resizing) .page-wrapper { 138 | transition: margin-right 0.3s ease, transform 0.3s ease; /* Animation: slide away */ 139 | } 140 | 141 | .content { 142 | overflow-y: auto; 143 | padding: 0 5px 50px 5px; 144 | } 145 | .content main { 146 | margin-inline-start: auto; 147 | margin-inline-end: auto; 148 | max-width: var(--content-max-width); 149 | } 150 | .content p { line-height: 1.45em; } 151 | .content ol { line-height: 1.45em; } 152 | .content ul { line-height: 1.45em; } 153 | .content a { text-decoration: none; } 154 | .content a:hover { text-decoration: underline; } 155 | .content img, .content video { max-width: 100%; } 156 | .content .header:link, 157 | .content .header:visited { 158 | color: var(--fg); 159 | } 160 | .content .header:link, 161 | .content .header:visited:hover { 162 | text-decoration: none; 163 | } 164 | 165 | table { 166 | margin: 0 auto; 167 | border-collapse: collapse; 168 | } 169 | table td { 170 | padding: 3px 16px; 171 | border: 1px var(--table-border-color) solid; 172 | } 173 | table thead { 174 | background: var(--table-header-bg); 175 | } 176 | table thead td { 177 | font-weight: 700; 178 | border: none; 179 | } 180 | table thead th { 181 | padding: 3px 16px; 182 | } 183 | table thead tr { 184 | border: 1px var(--table-header-bg) solid; 185 | } 186 | /* Alternate background colors for rows */ 187 | table tbody tr:nth-child(2n) { 188 | background: var(--table-alternate-bg); 189 | } 190 | 191 | 192 | blockquote { 193 | margin: 20px 0; 194 | padding: 0 20px; 195 | color: var(--fg); 196 | background-color: var(--quote-bg); 197 | border-block-start: .1em solid var(--quote-border); 198 | border-block-end: .1em solid var(--quote-border); 199 | } 200 | 201 | .warning { 202 | margin: 20px; 203 | padding: 0 20px; 204 | border-inline-start: 2px solid var(--warning-border); 205 | } 206 | 207 | .warning:before { 208 | position: absolute; 209 | width: 3rem; 210 | height: 3rem; 211 | margin-inline-start: calc(-1.5rem - 21px); 212 | /*content: "ⓘ";*/ 213 | text-align: center; 214 | background-color: var(--bg); 215 | color: var(--warning-border); 216 | font-weight: bold; 217 | font-size: 2rem; 218 | } 219 | 220 | blockquote .warning:before { 221 | background-color: var(--quote-bg); 222 | } 223 | 224 | kbd { 225 | background-color: var(--table-border-color); 226 | border-radius: 4px; 227 | border: solid 1px var(--theme-popup-border); 228 | box-shadow: inset 0 -1px 0 var(--theme-hover); 229 | display: inline-block; 230 | font-size: var(--code-font-size); 231 | font-family: var(--mono-font); 232 | line-height: 10px; 233 | padding: 4px 5px; 234 | vertical-align: middle; 235 | } 236 | 237 | :not(.footnote-definition) + .footnote-definition, 238 | .footnote-definition + :not(.footnote-definition) { 239 | margin-block-start: 2em; 240 | } 241 | .footnote-definition { 242 | font-size: 0.9em; 243 | margin: 0.5em 0; 244 | } 245 | .footnote-definition p { 246 | display: inline; 247 | } 248 | 249 | .tooltiptext { 250 | position: absolute; 251 | visibility: hidden; 252 | color: #fff; 253 | background-color: #333; 254 | transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ 255 | left: -8px; /* Half of the width of the icon */ 256 | top: -35px; 257 | font-size: 0.8em; 258 | text-align: center; 259 | border-radius: 6px; 260 | padding: 5px 8px; 261 | margin: 5px; 262 | z-index: 1000; 263 | } 264 | .tooltipped .tooltiptext { 265 | visibility: visible; 266 | } 267 | 268 | .chapter li.part-title { 269 | color: var(--sidebar-fg); 270 | margin: 5px 0px; 271 | font-weight: bold; 272 | } 273 | 274 | .result-no-output { 275 | font-style: italic; 276 | } 277 | 278 | /* -------------------------------------------------------------- */ 279 | 280 | /* Style sheet for TONC */ 281 | 282 | dfn { 283 | font-style: italic; 284 | font-weight: bold; 285 | } 286 | 287 | div.note { 288 | border-color: var(--fg); 289 | background-color: var(--table-alternate-bg); 290 | } 291 | div.note p, div.note ul { 292 | margin: 6px 0; 293 | } 294 | div.note ul { 295 | padding-left: 20px; 296 | } 297 | div.note p + p { 298 | margin-top: 12px; 299 | } 300 | 301 | div.nh, div.nhgood, div.nhbad, div.nhcare { 302 | font-weight: bold; 303 | margin-top: 5px; 304 | padding-bottom: 2px; 305 | border-bottom: 1px solid var(--quote-border); 306 | } 307 | 308 | /* TODO: can we use colours from the syntax highlighting style instead? */ 309 | /* (or just tweak them a bit per-theme) */ 310 | div.nhbad, .rem, .ack { 311 | color: #ec1256; 312 | } 313 | div.nhgood { 314 | color: #1ec36d; 315 | } 316 | div.nhcare { 317 | color: #e88f42; 318 | } 319 | .hljs span.rem, .hljs span.rem * { 320 | color: #ec1256; 321 | } 322 | .wavy { 323 | text-decoration: underline wavy; 324 | } 325 | 326 | .bold { 327 | font-weight: bold; 328 | } 329 | 330 | /* --- picture+caption frames --- */ 331 | div.cpt, div.cpt_fl, div.cpt_fr { 332 | margin: .5em; padding: 4px; 333 | border: 1px var(--table-border-color) solid; 334 | background-color: var(--table-alternate-bg); 335 | font-size: 80%; 336 | page-break-inside: avoid; 337 | } 338 | 339 | div.cpt_fl { float:left; } 340 | div.cpt_fr { float:right; } 341 | 342 | /* Margins inside tables work differently. */ 343 | td div.cpt, td div.dpt_fl, td div.cpt_fr { 344 | margin: 0; 345 | } 346 | 347 | .cpt p, .cpt_fl p, .cpt_fr p { 348 | margin: 0.5em 0; 349 | } 350 | 351 | /* --- table block styles --- */ 352 | /* yes, table and caption forms are required */ 353 | 354 | div.reg { margin: 8px auto; text-align: center; width: 94%; } 355 | div.reg table { margin: 1px auto; margin-bottom: 12px; text-align: left; } 356 | div.reg caption { margin: 0px auto; text-align: left; } 357 | 358 | div.cblock { margin: 6px auto; text-align: center; } 359 | div.cblock div { margin: 2px auto; text-align: left; } 360 | div.cblock table { margin: 2px auto; text-align: left; } 361 | div.cblock caption { margin: 0px auto; text-align: left; } 362 | 363 | /* div.lblock { margin: 6px 3em; } 364 | div.lblock table { margin: 2px 0; } */ 365 | 366 | div.cblock div.cpt { margin: 0.5em auto; } 367 | 368 | /* Fix oversight in the mdBook styles? */ 369 | table, table th { 370 | border: 1px var(--table-border-color) solid; 371 | } 372 | 373 | /* Gotta make register diagram table cells smaller so they fit on the page. */ 374 | .table-reg th, 375 | .table-reg td { 376 | padding: 3px 13px; 377 | } 378 | /* But some are too big even still. */ 379 | table.reg-huge th, 380 | table.reg-huge td { 381 | padding: 3px 7px; 382 | } 383 | 384 | /* === Table styles === */ 385 | 386 | td.fill, th.fill { width: 16px; } 387 | 388 | /* --- reg table --- */ 389 | .table-reg { 390 | font: var(--mono-font-size) var(--mono-font); 391 | page-break-inside: avoid; 392 | } 393 | caption.reg { 394 | caption-side:top; 395 | padding-bottom: 4px; 396 | } 397 | caption.reg, caption.reg * { 398 | font-size: 1.2rem; 399 | } 400 | caption[align="bottom"], div.cblock caption[align="bottom"] { 401 | margin-top: 6px; 402 | } 403 | 404 | caption.reg, tr.bits { 405 | white-space: nowrap; 406 | } 407 | 408 | tr.bits, tr.bf, col.bits, col.bf, col.def { text-align: center; } 409 | col.bits, col.bf, col.def { vertical-align: top; } 410 | tr.bits, col.bits { font: var(--mono-font-size) var(--mono-font); } 411 | tr.bf, col.bf { font-weight: bold; } 412 | col.def { font: var(--mono-font-size) var(--mono-font); } 413 | 414 | /* read only, write only */ 415 | .rof { text-decoration: overline; color: #f22; } 416 | .wof { text-decoration: underline; color: #44f; } 417 | 418 | /* reg colors */ 419 | .rclr0 { color: #ef0d4b; } 420 | .rclr1 { color: #41adff; } 421 | .rclr2 { color: #45e774; } 422 | .rclr3 { color: #d948d9; } 423 | .rclr4 { color: #f9663e; } 424 | .rclr5 { color: #d09a4c; } 425 | .rclr6 { color: #e7849b; } 426 | .rclr7 { color: #dce24b; } 427 | .rclr8 { color:teal; } 428 | .rclr9 { color:gray; } 429 | 430 | font[color="blue"] { color: #43a8e6; } 431 | font[color="green"] { color: #45e774; } 432 | font[color="red"] { color: #ef0d4b; } 433 | 434 | /* Special case table overrides (in Text systems) */ 435 | #fig\:img-fontpack, #tbl\:bupshade { 436 | 437 | & table { 438 | width: 100%; 439 | font-size: 90%; 440 | page-break-inside: avoid; 441 | } 442 | 443 | & span.rarr { 444 | font-size:200%; 445 | position: relative; 446 | top: 0.26em; 447 | } 448 | 449 | & td, th { 450 | padding: 1px 0; 451 | } 452 | 453 | & tr td, tr th { 454 | background-color: var(--table-alternate-bg); 455 | border: none; 456 | text-align: center; 457 | } 458 | 459 | & .table-reg th, .table-reg td { 460 | padding: 1px 3px; 461 | font-family: 'SourceSans3'; 462 | } 463 | 464 | & .bdrT, .bdrTL, .bdrTR, .bdrLL, .bdrRR { 465 | border-top: 1px var(--fg) solid; 466 | } 467 | 468 | & .bdrL, .bdrTL, .bdrBL, .bdrLL { 469 | border-left: 1px var(--fg) solid; 470 | } 471 | & .bdrB, .bdrBL, .bdrBR, .bdrLL, .bdrRR { 472 | border-bottom: 1px var(--fg) solid; 473 | } 474 | 475 | & .bdrR, .bdrTR, .bdrBR, .bdrRR { 476 | border-right: 1px var(--fg) solid; 477 | } 478 | } 479 | 480 | #tbl\:bupshade { 481 | width: 300px; 482 | margin: 0px 10px; 483 | } 484 | 485 | /* --- */ 486 | 487 | .menu-title { 488 | font-weight: 400; 489 | font-size: 2.75rem; 490 | letter-spacing: -0.02em; 491 | /* equals -2% */ 492 | } 493 | 494 | .box { 495 | margin: 20px 0; 496 | padding: 1.6px 20px; 497 | border-left-width: 0.75rem; 498 | border-left-style: solid; 499 | } 500 | 501 | .box > .box-title { 502 | font-weight: 600; 503 | text-transform: uppercase; 504 | } 505 | 506 | .box.tip { 507 | border-color: #42b983; 508 | background-color: var(--quote-bg); 509 | } 510 | 511 | .box.warning { 512 | border-left-color: var(--c-warning); 513 | background-color: rgba(255, 229, 100, 0.2); 514 | color: var(--c-warning-text); 515 | } 516 | 517 | .box.warning > .box-title { 518 | color: var(--c-warning-title); 519 | } 520 | 521 | .box.danger { 522 | border-left-color: var(--c-danger); 523 | background-color: rgba(255, 0, 0, .15); 524 | color: var(--c-danger-text); 525 | } 526 | 527 | .box.danger > .box-title { 528 | color: var(--c-danger-title); 529 | } 530 | 531 | .spacer { 532 | width: 100%; 533 | height: 3px; 534 | margin: 25px 0px 25px 0px; 535 | } 536 | 537 | /* Global CSS variables */ 538 | 539 | :root { 540 | /* (Override) workaround to get hover+active sidebar links on normal links blue */ 541 | --sidebar-active: var(--links); 542 | /* Warning boxes base styling */ 543 | --c-warning: #e7c000; 544 | --c-warning-bg: #fffae3; 545 | --c-warning-title: #ad9000; 546 | /* Danger boxes base styling */ 547 | --c-danger: #cc0000; 548 | --c-danger-text: #660000; 549 | --c-danger-title: #990000; 550 | } 551 | 552 | /* Theme-wide CSS variables */ 553 | 554 | .ayu { 555 | --c-warning-text: #eac100; 556 | --c-danger-text: #ff2929; 557 | --c-danger-title: #dc0000; 558 | } 559 | 560 | .coal { 561 | --c-warning-text: #eac100; 562 | --c-danger-text: #ff2929; 563 | --c-danger-title: #dc0000; 564 | } 565 | 566 | .light { 567 | --c-warning-text: #746000; 568 | } 569 | 570 | .navy { 571 | --c-warning-text: #eac100; 572 | --c-danger-text: #ff2929; 573 | --c-danger-title: #dc0000; 574 | } 575 | 576 | .rust { 577 | --c-warning-text: #ad8e00; 578 | } 579 | 580 | -------------------------------------------------------------------------------- /theme/css/tonc.css: -------------------------------------------------------------------------------- 1 | /* Tonc Classic theme */ 2 | 3 | .tonc { 4 | --bg: #c0f0f0; 5 | --fg: hsl(0, 0%, 0%); 6 | 7 | --sidebar-bg: #c0f0f0; 8 | --sidebar-fg: hsl(0, 0%, 0%); 9 | --sidebar-non-existant: #aaaaaa; 10 | --sidebar-active: #1f1fff; 11 | --sidebar-spacer: #a3e4e5; 12 | 13 | --scrollbar: #8F8F8F; 14 | 15 | --icons: #000060; 16 | --icons-hover: #000000; 17 | 18 | --links: blue; 19 | 20 | --inline-code-color: #301900; 21 | 22 | --theme-popup-bg: #c0f0f0; 23 | --theme-popup-border: #80d6d7; 24 | --theme-hover: #a3e4e5; 25 | 26 | --quote-bg: hsl(197, 37%, 96%); 27 | --quote-border: hsl(197, 37%, 91%); 28 | 29 | 30 | --table-border-color: #808080; 31 | --table-background-color: #e0e0e0; 32 | --table-background-color-custom: #d0d0d0; 33 | --table-header-bg: unset; 34 | --table-alternate-bg: #B0B0B0; 35 | 36 | --searchbar-border-color: #aaa; 37 | --searchbar-bg: #fafafa; 38 | --searchbar-fg: #000; 39 | --searchbar-shadow-color: #aaa; 40 | --searchresults-header-fg: #000060; 41 | --searchresults-border-color: #888; 42 | --searchresults-li-bg: #e4f2fe; 43 | --search-mark-bg: #a2cff5; 44 | 45 | --color-scheme: light; 46 | 47 | /* Extra */ 48 | --font: Verdana, Geneva, Tahoma, sans-serif; 49 | --font-size: 13px; 50 | --warning-border: #ffe00; 51 | --h2-color: green; 52 | --h3-color: #000060; 53 | } 54 | 55 | .tonc { 56 | background-color: #c0f0f0; 57 | font-family: var(--font); 58 | letter-spacing: 0px; 59 | } 60 | 61 | .tonc .content { 62 | background-image: url(../../img/main/tonc_bg.png); 63 | } 64 | 65 | .tonc .content p, 66 | .tonc .content ul { 67 | line-height: normal; 68 | } 69 | 70 | .tonc #menu-bar.bordered { 71 | border-block-end-color: gray; 72 | border-width: 2px; 73 | border-bottom: groove; 74 | } 75 | 76 | .tonc .box, 77 | .tonc .note { 78 | width: 92%; 79 | margin: 0.7em auto; 80 | padding: 8px; 81 | background-color: #E0E0E0; 82 | border: 2px solid #FF0000; 83 | page-break-inside: avoid; 84 | border-radius: 8px; 85 | box-shadow: 2px 2px 4px #808080; 86 | } 87 | 88 | .tonc blockquote { 89 | display: block; 90 | margin-block-start: 1em; 91 | margin-block-end: 1em; 92 | margin-inline-start: 40px; 93 | margin-inline-end: 40px; 94 | background-color: unset; 95 | border: none; 96 | } 97 | 98 | .tonc body .page .content { 99 | font-size: var(--font-size); 100 | } 101 | 102 | /* Link text */ 103 | .tonc .sidebar a:visited { 104 | color: #551a8b; 105 | } 106 | 107 | .tonc .sidebar a, .tonc ul a { 108 | color: blue; 109 | text-decoration: underline; 110 | line-height: normal; 111 | } 112 | 113 | .tonc .sidebar a:active { 114 | color: red; 115 | } 116 | 117 | .tonc .sidebar { 118 | font-size: var(--font-size) !important; 119 | } 120 | 121 | .tonc .content a:active { 122 | color: red; 123 | } 124 | 125 | .tonc .box > .box-title { 126 | margin-top: 0; 127 | font-size: 115%; 128 | font-weight: bold; 129 | margin: 0.1em 0.5em 0.2em; 130 | text-transform: none; 131 | } 132 | 133 | .tonc .box > p { 134 | margin-block-start: 0px; 135 | margin-block-end: 0px; 136 | color: var(--fg); 137 | } 138 | 139 | .tonc .box.note > .box-title { 140 | color: var(--fg); 141 | } 142 | 143 | .tonc .chapter li.part-title, 144 | .tonc .box.tip > .box-title { 145 | color: green; 146 | } 147 | 148 | .tonc .box.warning > .box-title { 149 | color: #c06000; 150 | } 151 | 152 | .tonc .box.danger > .box-title { 153 | color: #ff0000; 154 | } 155 | 156 | /* Headers */ 157 | .tonc h2::before { 158 | color: var(--h2-color); 159 | } 160 | 161 | .tonc h3::before { 162 | color: var(--h3-color); 163 | } 164 | 165 | .tonc h2, h3 { 166 | margin-block-start: 1em; 167 | margin-block-end: 0em; 168 | } 169 | 170 | .tonc .content h1 .header { 171 | color: blue; 172 | } 173 | 174 | .tonc .content h2 .header { 175 | margin: 1em 1em 0.2em; 176 | font-size: 90%; 177 | color: var(--h2-color); 178 | } 179 | 180 | .tonc .content h3 .header { 181 | margin: 1em 1em 0.2em; 182 | font-size: 90%; 183 | color: #000060; 184 | } 185 | 186 | /* Code block style */ 187 | .tonc pre code { 188 | border: #C0C0C0 solid 1px; 189 | width: 92%; 190 | margin: 0.6em auto; 191 | padding: 8px; 192 | background: #E0E0E0 !important; 193 | border-radius: 4px; 194 | box-shadow: 2px 2px 4px #C0C0C0; 195 | font: 500 100% "Courier New", monospace !important; 196 | } 197 | 198 | /* Clipboard icon on code */ 199 | .tonc pre > .buttons :hover { 200 | color: var(--searchresults-header-fg); 201 | border-color: var(--searchresults-header-fg); 202 | background-color: #c0c0c0; 203 | } 204 | 205 | .tonc pre > .buttons button { 206 | border-color: var(--scrollbar); 207 | background-color: #e0e0e0; 208 | transition: 100ms; 209 | transition-property: color,border-color,background-color; 210 | color: var(--scrollbar); 211 | } 212 | 213 | /* Syntax highlighting on code */ 214 | .tonc .hljs { 215 | color: #202040; 216 | font-family: monospace; 217 | background: none; 218 | } 219 | 220 | .tonc .hljs span.rem, 221 | .tonc .hljs span.rem * { 222 | color: red; 223 | } 224 | 225 | .tonc .hljs-comment, 226 | .tonc .hljs-quote { 227 | color: green; 228 | } 229 | 230 | .tonc .hljs-keyword, 231 | .tonc .hljs-meta, 232 | .tonc .hljs-meta-keyword { 233 | color: #0000D0; 234 | } 235 | 236 | .tonc .hljs-number { 237 | color: #C000C0; 238 | } 239 | 240 | .tonc .hljs-title, 241 | .tonc .hljs-params { 242 | color: #000000; 243 | } 244 | 245 | .tonc pre .buttons { 246 | margin-right: 21px; 247 | } 248 | 249 | /* Register colors */ 250 | .tonc .rclr0 { color:red; } 251 | .tonc .rclr1 { color:blue; } 252 | .tonc .rclr2 { color:#006000; } 253 | .tonc .rclr3 { color:magenta; } 254 | .tonc .rclr4 { color:brown; } 255 | .tonc .rclr5 { color:olive; } 256 | .tonc .rclr6 { color:purple; } 257 | .tonc .rclr7 { color:#C06000; } 258 | .tonc .rclr8 { color:teal; } 259 | .tonc .rclr9 { color:gray; } 260 | 261 | /* Override default table style */ 262 | .tonc table, 263 | .tonc table th, 264 | .tonc table td { 265 | border: none; 266 | margin-left: 0px; 267 | font-size: var(--font-size); 268 | } 269 | 270 | .tonc td { 271 | padding: 2px; 272 | } 273 | 274 | .tonc table { 275 | border-collapse: separate; 276 | text-indent: initial; 277 | line-height: normal; 278 | font-weight: normal; 279 | font-size: unset; 280 | font-style: normal; 281 | text-align: start; 282 | border-spacing: 0px; 283 | white-space: normal; 284 | font-variant: normal; 285 | } 286 | 287 | /* Disable default alternate background colors for rows */ 288 | .tonc table tbody tr:nth-child(2n) { 289 | background: none; 290 | } 291 | 292 | /* Figures, captions, blocks */ 293 | .tonc .cpt, 294 | .tonc .cpt_fl, 295 | .tonc .cpt_fr { 296 | padding: 0.4em; 297 | border: 1px var(--fg) solid; 298 | background-color: unset; 299 | } 300 | 301 | .tonc .cpt .table-data, 302 | .tonc .cpt_fl .table-data, 303 | .tonc .cpt_fr .table-data { 304 | font-size: var(--font-size); 305 | } 306 | 307 | .tonc caption { 308 | font-size: 80%; 309 | text-align:left; 310 | margin-left: 1em; } 311 | 312 | .tonc div.lblock { margin: 6px 3em; } 313 | .tonc div.lblock table { margin: 2px 0; } 314 | 315 | /* Special case table overrides (in Text systems) */ 316 | .tonc #fig\:img-fontpack { 317 | 318 | & table { 319 | font-family: var(--font); 320 | } 321 | & tr td, tr th { 322 | background-color: unset; 323 | } 324 | 325 | & .table-reg th, .table-reg td { 326 | padding: 1px 3px; 327 | font-family: var(--font); 328 | } 329 | } 330 | 331 | .tonc #tbl\:bupshade { 332 | width: unset; 333 | font-family: var(--font); 334 | font-size: 120%; 335 | margin: 0px 10px; 336 | 337 | & tr td, tr th { 338 | background-color: var(--table-background-color); 339 | padding: 2px 4px; 340 | } 341 | } 342 | 343 | .tonc #fig\:img-m7-ex { 344 | border: 1px var(--fg) solid; 345 | } 346 | 347 | .tonc #tbl\:culltest mtable, 348 | .tonc #tbl\:culltest mtr, 349 | .tonc #tbl\:culltest mtd { 350 | padding: 0px 0px; 351 | } 352 | 353 | /* Equations */ 354 | .tonc .eqnrcell { 355 | font-weight: bold; 356 | } 357 | 358 | .tonc .eqcell { 359 | padding-left: 1em; 360 | } 361 | 362 | 363 | /* Normal data tables */ 364 | .tonc table.table-data { 365 | border: 1px outset var(--table-border-color); 366 | } 367 | 368 | .tonc table.table-data th, 369 | .tonc table.table-data tr, 370 | .tonc table.table-data td { 371 | border: 1px inset var(--table-border-color); 372 | } 373 | 374 | /* Regular register tables */ 375 | .tonc .table-reg tbody { 376 | background-color: #e0e0e0; 377 | } 378 | 379 | .tonc .table-reg { 380 | font: 90% "Courier New", monospace; 381 | background: #E0E0E0; 382 | border: solid #C0C0C0 1px; 383 | page-break-inside: avoid; 384 | } 385 | 386 | .tonc .table-reg th, 387 | .tonc .table-reg td { 388 | border: 1px inset #c0c0c0; 389 | padding: 4px 4px; 390 | } 391 | 392 | /* Tall register tables */ 393 | .tonc table.table-reg-vert { 394 | border-collapse: separate; 395 | text-indent: initial; 396 | line-height: normal; 397 | font-weight: normal; 398 | font-size: medium; 399 | font-style: normal; 400 | text-align: start; 401 | border-spacing: 2px; 402 | border: none; 403 | white-space: normal; 404 | font-variant: normal; 405 | } 406 | 407 | .tonc table.table-reg-vert th { 408 | border: none; 409 | background-color: none; 410 | } 411 | 412 | .tonc table.table-reg-vert tr:nth-child(1n) td{ 413 | border: none; 414 | background-color: #e0e0e0; 415 | border-bottom: #c0c0c0 1px solid; 416 | } 417 | 418 | .tonc table.table-reg-vert tr:nth-child(2n) td{ 419 | border: none; 420 | background-color: #c0c0c0; 421 | border-bottom: #e0e0e0 1px solid; 422 | } 423 | 424 | /* Special case table (in Regular tiled backgrounds) */ 425 | .tonc #tbl\:cbb-sbb th, 426 | .tonc #tbl\:cbb-sbb td { 427 | padding: 3px 3px; 428 | } 429 | 430 | .tonc table[rules=groups i] { 431 | border-collapse: collapse; 432 | } 433 | 434 | /* Rules cols equivalent, only column borders not rows */ 435 | .tonc table.rules-cols td, 436 | .tonc table.rules-cols th { 437 | border-top: none; 438 | border-left: none; 439 | border-bottom:none; 440 | } 441 | 442 | .tonc table.rules-cols tr th:last-child, 443 | .tonc table.rules-cols tr td:last-child { 444 | border-right: none; 445 | } 446 | 447 | /* Frame void and rules groups equivalent */ 448 | .tonc table.rules-groups th { 449 | border-top: none; 450 | border-left: none; 451 | border-right: 1px solid var(--fg); 452 | } 453 | 454 | .tonc table.rules-groups td { 455 | border-top: none; 456 | border-left: none; 457 | border-bottom:none; 458 | border-right: 1px solid var(--fg); 459 | } 460 | 461 | .tonc table.rules-groups { 462 | border: none; 463 | } 464 | 465 | .tonc table.rules-groups tr th:last-child, 466 | .tonc table.rules-groups tr td:last-child { 467 | border-right: none; 468 | } 469 | 470 | .tonc table.rules-groups tbody tr th { 471 | border-bottom: none; 472 | } 473 | 474 | /* Misc */ 475 | .tonc table.border-none { 476 | border: none; 477 | } 478 | 479 | .tonc table.border-none th, 480 | .tonc table.border-none td { 481 | border: none; 482 | } 483 | 484 | .tonc tr.bits, tr.bf, col.bits, col.bf, col.def { text-align:center; } 485 | 486 | .tonc col.bits, col.bf, col.def { vertical-align:top; } 487 | .tonc tr.bits, col.bits { font: 90% "Courier New"; } 488 | .tonc tr.bf, col.bf { font-weight:bold; } 489 | .tonc col.def { font: 90% "Courier New"; } 490 | 491 | .tonc .center { 492 | margin: auto; 493 | } 494 | 495 | .tonc tt { 496 | color: #400080; 497 | } 498 | 499 | .tonc kbd { 500 | color: #202040; 501 | font-family: monospace; 502 | background: none; 503 | border: none; 504 | box-shadow: none; 505 | } 506 | 507 | .tonc .bdr { 508 | border: 1px var(--fg) solid; 509 | } 510 | 511 | .tonc .bdrB, .bdrBL, .bdrBR, .bdrLL, .bdrRR { 512 | border-bottom: 1px black solid; 513 | } -------------------------------------------------------------------------------- /theme/head.hbs: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | -------------------------------------------------------------------------------- /theme/index.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ title }} 7 | {{#if is_print }} 8 | 9 | {{/if}} 10 | {{#if base_url}} 11 | 12 | {{/if}} 13 | 14 | 15 | 16 | {{> head}} 17 | 18 | 19 | 20 | 21 | 22 | {{#if favicon_svg}} 23 | 24 | {{/if}} 25 | {{#if favicon_png}} 26 | 27 | {{/if}} 28 | 29 | 30 | 31 | {{#if print_enable}} 32 | 33 | {{/if}} 34 | 35 | 36 | 37 | {{#if copy_fonts}} 38 | 39 | {{/if}} 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {{#each additional_css}} 49 | 50 | {{/each}} 51 | 52 | {{#if mathjax_support}} 53 | 54 | 55 | {{/if}} 56 | 57 | 58 |
59 | 60 | 64 | 65 | 66 | 80 | 81 | 82 | 93 | 94 | 95 | 96 | 97 | 111 | 112 | 120 | 121 | 122 | 142 | 143 |
144 | 145 |
146 | {{> header}} 147 | 148 | 194 | 195 | {{#if search_enabled}} 196 | 206 | {{/if}} 207 | 208 | 209 | 216 | 217 |
218 |
219 | {{{ content }}} 220 |
221 | 222 | 238 |
239 |
240 | 241 | 254 | 255 |
256 | 257 | {{#if live_reload_endpoint}} 258 | 259 | 274 | {{/if}} 275 | 276 | {{#if google_analytics}} 277 | 278 | 293 | {{/if}} 294 | 295 | {{#if playground_line_numbers}} 296 | 299 | {{/if}} 300 | 301 | {{#if playground_copyable}} 302 | 305 | {{/if}} 306 | 307 | {{#if playground_js}} 308 | 309 | 310 | 311 | 312 | 313 | {{/if}} 314 | 315 | {{#if search_js}} 316 | 317 | 318 | 319 | {{/if}} 320 | 321 | 322 | 323 | 324 | 325 | 326 | {{#each additional_js}} 327 | 328 | {{/each}} 329 | 330 | {{#if is_print}} 331 | {{#if mathjax_support}} 332 | 339 | {{else}} 340 | 345 | {{/if}} 346 | {{/if}} 347 | 348 |
349 | 350 | 351 | -------------------------------------------------------------------------------- /theme/templates/archives.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ SITENAME }} - Archives{% endblock %} 4 | 5 | {% block content %} 6 |

Archives for {{ SITENAME }}

7 | 8 |
9 | {% for article in dates %} 10 |
{{ article.locale_date }}
11 |
{{ article.title }}
12 | {% endfor %} 13 |
14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /theme/templates/article.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block html_lang %}{{ article.lang }}{% endblock %} 3 | 4 | {% block title %}{{ SITENAME }} - {{ article.title|striptags }}{% endblock %} 5 | 6 | {% block head %} 7 | {{ super() }} 8 | 9 | {% import 'translations.html' as translations with context %} 10 | {% if translations.entry_hreflang(article) %} 11 | {{ translations.entry_hreflang(article) }} 12 | {% endif %} 13 | 14 | {% if article.description %} 15 | 16 | {% endif %} 17 | 18 | {% for tag in article.tags %} 19 | 20 | {% endfor %} 21 | 22 | {% endblock %} 23 | 24 | {% block content %} 25 |
26 |

27 | {{ article.title }}

29 | {% import 'translations.html' as translations with context %} 30 | {{ translations.translations_for(article) }} 31 |
32 |
33 | 36 | {% if article.modified %} 37 | 40 | {% endif %} 41 | {% if article.authors %} 42 |
43 | By {% for author in article.authors %} 44 | {{ author }} 45 | {% endfor %} 46 |
47 | {% endif %} 48 | {% if article.category %} 49 |
50 | Category: {{ article.category }} 51 |
52 | {% endif %} 53 | {% if article.tags %} 54 |
55 | Tags: 56 | {% for tag in article.tags %} 57 | {{ tag }} 58 | {% endfor %} 59 |
60 | {% endif %} 61 |
62 |
63 | {{ article.content }} 64 |
65 | {% endblock %} 66 | -------------------------------------------------------------------------------- /theme/templates/author.html: -------------------------------------------------------------------------------- 1 | {% extends "index.html" %} 2 | 3 | {% block title %}{{ SITENAME }} - Articles by {{ author }}{% endblock %} 4 | 5 | {% block content_title %} 6 |

Articles by {{ author }}

7 | {% endblock %} 8 | 9 | -------------------------------------------------------------------------------- /theme/templates/authors.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ SITENAME }} - Authors{% endblock %} 4 | 5 | {% block content %} 6 |

Authors on {{ SITENAME }}

7 |
    8 | {% for author, articles in authors|sort %} 9 |
  • {{ author }} ({{ articles|count }})
  • 10 | {% endfor %} 11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /theme/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block head %} 5 | {% block title %}{{ SITENAME }}{% endblock title %} 6 | 12 | 13 | 14 | 15 | {% if FEED_ALL_ATOM %} 16 | 17 | {% endif %} 18 | {% if FEED_ALL_RSS %} 19 | 20 | {% endif %} 21 | {% if FEED_ATOM %} 22 | 23 | {% endif %} 24 | {% if FEED_RSS %} 25 | 26 | {% endif %} 27 | {% if CATEGORY_FEED_ATOM and category %} 28 | 29 | {% endif %} 30 | {% if CATEGORY_FEED_RSS and category %} 31 | 32 | {% endif %} 33 | {% if TAG_FEED_ATOM and tag %} 34 | 35 | {% endif %} 36 | {% if TAG_FEED_RSS and tag %} 37 | 38 | {% endif %} 39 | {% endblock head %} 40 | 41 | 42 | 43 | 44 | 70 |
71 | {% block content %} 72 | {% endblock %} 73 |
74 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /theme/templates/categories.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ SITENAME }} - Categories{% endblock %} 4 | 5 | {% block content %} 6 |

Categories on {{ SITENAME }}

7 |
    8 | {% for category, articles in categories|sort %} 9 |
  • {{ category }} ({{ articles|count }})
  • 10 | {% endfor %} 11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /theme/templates/category.html: -------------------------------------------------------------------------------- 1 | {% extends "index.html" %} 2 | 3 | {% block title %}{{ SITENAME }} - {{ category }} category{% endblock %} 4 | 5 | {% block content_title %} 6 |

Articles in the {{ category }} category

7 | {% endblock %} 8 | 9 | -------------------------------------------------------------------------------- /theme/templates/gosquared.html: -------------------------------------------------------------------------------- 1 | {% if GOSQUARED_SITENAME %} 2 | 14 | {% endif %} 15 | -------------------------------------------------------------------------------- /theme/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 | {% block content_title %} 5 |

All articles

6 | {% endblock %} 7 | 8 |
    9 | {% for article in articles_page.object_list %} 10 |
  1. 11 |

    {{ article.title }}

    12 |
    13 | 14 |
    By 15 | {% for author in article.authors %} 16 | {{ author }} 17 | {% endfor %} 18 |
    19 |
    20 |
    {{ article.summary }}
    21 |
  2. 22 | {% endfor %} 23 |
24 | {% if articles_page.has_other_pages() %} 25 | {% include 'pagination.html' %} 26 | {% endif %} 27 |
28 | {% endblock content %} 29 | -------------------------------------------------------------------------------- /theme/templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block html_lang %}{{ page.lang }}{% endblock %} 3 | 4 | {% block title %}{{ SITENAME }} - {{ page.title|striptags }}{%endblock%} 5 | 6 | {% block head %} 7 | {{ super() }} 8 | 9 | {% import 'translations.html' as translations with context %} 10 | {% if translations.entry_hreflang(page) %} 11 | {{ translations.entry_hreflang(page) }} 12 | {% endif %} 13 | {% endblock %} 14 | 15 | {% block content %} 16 | 17 | {% set (prev_num, prev_slug, prev_name) = PREV(page) %} 18 | {% set (next_num, next_slug, next_name) = NEXT(page) %} 19 | 20 | 21 | 22 | 27 | 28 | 33 | 34 |
23 | {% if prev_num != None %} 24 | {{ prev_name }} 25 | {% endif %} 26 | Contents 29 | {% if next_num != None %} 30 | {{ next_name }} 31 | {% endif %} 32 |
35 | 36 |
37 | 38 | 39 | 40 | {{ page.content }} 41 | 42 |
43 | 44 |
45 | By J Vijn + gbadev.net community. 46 | Modified: {{ page.modified.strftime("%Y-%m-%d") }}. 47 | Get Tonc example code here. 48 |
49 | 50 |
51 | 52 | 53 | 54 | 59 | 60 | 65 | 66 | 67 | 72 | 73 | 78 | 79 | 80 | 81 | {% endblock %} 82 | -------------------------------------------------------------------------------- /theme/templates/pagination.html: -------------------------------------------------------------------------------- 1 | {% if DEFAULT_PAGINATION %} 2 | {% set first_page = articles_paginator.page(1) %} 3 | {% set last_page = articles_paginator.page(articles_paginator.num_pages) %} 4 |

5 | {% if articles_page.has_previous() %} 6 | 7 | « 8 | {% endif %} 9 | Page {{ articles_page.number }} / {{ articles_paginator.num_pages }} 10 | {% if articles_page.has_next() %} 11 | » 12 | 13 | {% endif %} 14 |

15 | {% endif %} 16 | -------------------------------------------------------------------------------- /theme/templates/period_archives.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ SITENAME }} - {{ period | reverse | join(' ') }} archives{% endblock %} 4 | 5 | {% block content %} 6 |

Archives for {{ period | reverse | join(' ') }}

7 | 8 |
9 | {% for article in dates %} 10 |
{{ article.locale_date }}
11 |
{{ article.title }}
12 | {% endfor %} 13 |
14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /theme/templates/tag.html: -------------------------------------------------------------------------------- 1 | {% extends "index.html" %} 2 | 3 | {% block title %}{{ SITENAME }} - {{ tag }} tag{% endblock %} 4 | 5 | {% block content_title %} 6 |

Articles tagged with {{ tag }}

7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /theme/templates/tags.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ SITENAME }} - Tags{% endblock %} 4 | 5 | {% block content %} 6 |

Tags for {{ SITENAME }}

7 |
    8 | {% for tag, articles in tags|sort %} 9 |
  • {{ tag }} ({{ articles|count }})
  • 10 | {% endfor %} 11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /theme/templates/translations.html: -------------------------------------------------------------------------------- 1 | {% macro translations_for(article) %} 2 | {% if article.translations %} 3 | Translations: 4 | {% for translation in article.translations %} 5 | {{ translation.lang }} 6 | {% endfor %} 7 | {% endif %} 8 | {% endmacro %} 9 | 10 | {% macro entry_hreflang(entry) %} 11 | {% if entry.translations %} 12 | {% for translation in entry.translations %} 13 | 14 | {% endfor %} 15 | {% endif %} 16 | {% endmacro %} 17 | --------------------------------------------------------------------------------